文章目录
例题12 Another Crisis UVA - 12186 (树形dp)
题目链接
状态方程和转移: 将老板作为根,用
d
p
[
u
]
dp[u]
dp[u]表示命令会从
u
u
u向上级传导时的最少工人数,目标状态为
d
p
[
0
]
dp[0]
dp[0],起始状态为树的叶子,即工人们,直接返回1。将所有获得的孩子的
d
p
dp
dp值从小到大进行排序,按比例取前
c
n
t
cnt
cnt个。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e5+10;
int n,k;
int dp[N];//dp[i]表示让i签字的最少工人数量
vector<int>son[N];
int DP(int u){
int sz=son[u].size();
if(!sz)return dp[u]=1;//工人
if(dp[u])return dp[u];
int cnt=sz*k/100;
if(sz*k%100)cnt++;
vector<int>son_dp;
for(int i=0;i<sz;i++)son_dp.push_back(DP(son[u][i]));
sort(son_dp.begin(),son_dp.end());
for(int i=0;i<cnt;i++)dp[u]+=son_dp[i];
return dp[u];
}
void solve(){
memset(dp,0,sizeof dp);
for(int i=0;i<=n;i++)son[i].clear();
for(int i=1;i<=n;i++){
int x;cin>>x;
son[x].push_back(i);
}
cout<<DP(0)<<endl;
}
int main(){
while(cin>>n>>k&&n&&k)
solve();
return 0;
}
例题13 Party at Hali-Bula UVA - 1220 (树形dp模板)
题目链接
状态设计:
d
p
[
u
]
[
0
]
dp[u][0]
dp[u][0]表示以u为根的树中来的最多人数,同时
u
u
u不来,
d
p
[
u
]
[
1
]
dp[u][1]
dp[u][1]表示以u为根的树中来的最多人数,且
u
u
u来。因为还需要判断答案的唯一性,再用
d
p
2
[
u
]
[
0
]
/
d
p
2
[
u
]
[
1
]
dp2[u][0]/dp2[u][1]
dp2[u][0]/dp2[u][1],为真表示对应的
d
p
[
u
]
[
0
]
dp[u][0]
dp[u][0]不唯一。
#include<bits/stdc++.h>
using namespace std;
const int N=210;
map<string,int>ma;
int n;
vector<int>son[N];
int dp[N][2],cnt;
bool dp2[N][2];//如果是1表示方案不唯一
void dfs(int u){
if(!son[u].size()){
dp[u][1]=1,dp[u][0]=0;return;
}
dp[u][0]=0,dp[u][1]=1;
int fl=0,maxx=0;
for(int i=0;i<son[u].size();i++){
int v=son[u][i];
dfs(v);
//更新1
if(dp2[v][0])dp2[u][1]=1;
dp[u][1]+=dp[v][0];
//更新0
dp[u][0]+=max(dp[v][0],dp[v][1]);
if((dp[v][0]==dp[v][1])||(dp[v][0]>dp[v][1]&&dp2[v][0])||(dp[v][1]>dp[v][0]&&dp2[v][1]))
dp2[u][0]=1;
}
}
void solve(){
ma.clear();
for(int i=0;i<=n;i++)son[i].clear();
memset(dp,-1,sizeof dp);
memset(dp2,0,sizeof dp2);
string tmp;cin>>tmp;
ma[tmp]=cnt=1;//1是根
for(int i=1;i<n;i++){
string u,v;cin>>u>>v;
if(!ma[u])ma[u]=++cnt;
if(!ma[v])ma[v]=++cnt;
son[ma[v]].push_back(ma[u]);
}
son[0].push_back(1);
dfs(0);
cout<<dp[0][0]<<' ';
if(dp2[0][0])cout<<"No"<<endl;
else cout<<"Yes"<<endl;
}
int main(){
while(cin>>n&&n)
solve();
}
例题14 Perfect Service UVA - 1218(树形dp)
题目链接
题目大意: 是一棵树,每个点只能和恰好一个服务站相连,但是服务站本身可以和服务站和普通站点连接,求最少需要建立的服务站个数。
状态设计和转移: 设当前节点
u
u
u的子节点是
v
v
v,首先随便选节点1作为根,将无根树转化为有根树。接下来给节点分几种情况:
0. 本身不是服务站,父亲是服务站:孩子从1转移来,答案是
∑
d
p
[
v
]
[
1
]
\sum{dp[v][1]}
∑dp[v][1]
- 本身不是服务站,父亲不是服务站:恰好有一个孩子是服务站,假设孩子 k k k是服务站,答案是 ∑ v ! = k d p [ v ] [ 1 ] + d p [ k ] [ 2 ] \sum_{v!=k}{dp[v][1]}+dp[k][2] v!=k∑dp[v][1]+dp[k][2]我们其实可以直接计算 ∑ d p [ v ] [ 1 ] \sum{dp[v][1]} ∑dp[v][1],再在 u u u的孩子中选一个孩子 k k k使得 d p [ k ] [ 2 ] − d p [ k ] [ 1 ] dp[k][2]-dp[k][1] dp[k][2]−dp[k][1]的值最小,答案即是 ∑ d p [ v ] [ 1 ] + d p [ k ] [ 2 ] − d p [ k ] [ 1 ] \sum{dp[v][1]}+dp[k][2]-dp[k][1] ∑dp[v][1]+dp[k][2]−dp[k][1]
- 本身是服务站:孩子从0和2转移来,答案是 ∑ m i n ( d p [ v ] [ 0 ] , d p [ v ] [ 2 ] \sum{min(dp[v][0],dp[v][2]} ∑min(dp[v][0],dp[v][2]
然后注意设置最大值为 n n n而不要设为 i n f = 0 x 3 f 3 f 3 f 3 f inf=0x3f3f3f3f inf=0x3f3f3f3f,因为有的孩子可能无法转移过来,很多 i n f inf inf相加就爆了,或者可以开 l o n g l o n g long long longlong试试看。(wa得一脸懵逼)
#include<bits/stdc++.h>
#define LL long long
#define min3(a,b,c) min(min(a,b),c)
using namespace std;
const int N=1E4+7;
vector<int>son[N];
int n,dp[N][3];
void dfs(int u,int fa){
dp[u][0]=0,dp[u][2]=1;
int dif=n;
for(int i=0;i<son[u].size();i++){
int v=son[u][i];
if(v==fa)continue;
dfs(v,u);
dp[u][0]+=dp[v][1];
dp[u][2]+=min(dp[v][0],dp[v][2]);
dif=min(dif,dp[v][2]-dp[v][1]);
}
dp[u][1]=dp[u][0]+dif;
}
int main(){
while(cin>>n){
for(int i=0;i<=n;i++)dp[i][0]=dp[i][1]=dp[i][2]=n;
for(int i=0;i<=n;i++)son[i].clear();
son[1].push_back(0);
for(int i=1;i<n;i++){
int x,y;cin>>x>>y;
son[x].push_back(y),son[y].push_back(x);
}
dfs(1,0);
cout<<min(dp[1][1],dp[1][2])<<endl;
int tmp;cin>>tmp;
if(tmp==-1)break;
}
return 0;
}
例题15 Headmaster’s Headache UVA - 10817 (状态压缩,记忆化搜索)
题目链接
状态设计:
d
p
[
i
]
[
s
1
]
[
s
2
]
dp[i][s1][s2]
dp[i][s1][s2]表示前
i
i
i个人之后的最少花费(
i
i
i从0开始编号),
s
1
s1
s1表示有一个教师教授的课程集合,
s
2
s2
s2表示有两个教师教授的课程集合。起始状态是
s
1
,
s
2
s1,s2
s1,s2均为0,且一个人都没有考虑到,即
d
p
[
0
]
[
0
]
[
0
]
dp[0][0][0]
dp[0][0][0]。
状态转移:
s0:当前教师可以教的课程应该从集合里剔除。
s1:删去原来有一个人教的并且当前教师还会教的(因为这部分转移向s2); 同时加上原来没有人教的而当前教师会的。
s2:保留原来有两个人教; 再加上原来有一个人教并且当前的教师会教的。
其实写到现在还是对状态压缩不是特别理解,大概是用某个维度维护一个集合,进行集合的转移,直到想要的终态。
#include<bits/stdc++.h>
using namespace std;
const int N=200,S=8,inf=0x3f3f3f3f;
int dp[N][1<<S][1<<S],st[N],c[N];
int n,m,s;
int DP(int i,int s0,int s1,int s2){
if(i==n+m)return (s2==((1<<s)-1))?0:inf;
int &ans=dp[i][s1][s2];
if(ans>=0)return ans;
ans=inf;
if(i>=m)ans=DP(i+1,s0,s1,s2);
int m0=s0&st[i],m1=s1&st[i];
s0^=m0,s1=(s1^m1)|m0,s2|=m1;
return ans=min(ans,c[i]+DP(i+1,s0,s1,s2));
}
int main(){
string str;
while(getline(cin,str)){
stringstream ss(str);
ss>>s>>m>>n;
if(s==0)break;
for(int i=0;i<n+m;i++){
getline(cin,str);
stringstream ss(str);
ss>>c[i];
st[i]=0;
int x;
while(ss>>x)st[i]|=(1<<(x-1));
}
memset(dp,-1,sizeof dp);
cout<<DP(0,(1<<s)-1,0,0)<<endl;
}
}