树形dp
来源:互联网 发布:免费制作电子相册软件 编辑:程序博客网 时间:2024/05/16 18:01
OJ题目:click here~~
给一棵树,问至少砍掉几个树枝,能得到有m个结点的子树。
const int Max_N = 152;vector < int > List[Max_N];int n , m;int dp[Max_N][Max_N];void dfs(int u , int father){ dp[u][1] = 0; int i , j , k; for(i = 0;i < List[u].size();i++){ int v = List[u][i]; if(v == father) continue; dfs(v , u); for(j = m;j >= 1;j--){ dp[u][j]++; for(k = 1;k < j;k++){ dp[u][j] = min(dp[u][j] , dp[v][k] + dp[u][j - k]); } } }}int main(){ //freopen("in.txt","r",stdin); while(scanf("%d%d",&n,&m) != EOF){ int a , b , i , j , k ; for(i = 0;i <= n;i++) List[i].clear(); for(i = 1;i < n;i++){ scanf("%d%d",&a,&b); List[a].push_back(b); List[b].push_back(a); } memset(dp , 63 , sizeof(dp)); dfs(1 , -1); int ans = dp[1][m]; for(i = 2;i <= n;i++) ans = min(ans , dp[i][m] + 1); cout << ans << endl; }}
OJ题目:click here~~
用代价W[ i ]贿赂父亲结点,即可拥有该父亲结点,所有的子结点 和 子结点的子结点…… 求拥有m个结点所需要的最小代价。
const int maxn = 202;int n , m;map<string , int> h;vector<int> g[maxn];int in[maxn];int w[maxn];int dp[maxn][maxn];int son[maxn];void dfs(int u){ dp[u][0] = 0;//取0个结点的时候花费是0 son[u]++;//加入u自己 int i , j , k , v; for(i = 0;i < g[u].size();i++){//遍历u的子树 v = g[u][i]; dfs(v); for(j = n;j >= 0;j--){//树上的分组背包 for(k = 0;k <= j;k++){ dp[u][j] = min(dp[u][j] , dp[v][k] + dp[u][j - k]); } } son[u] += son[v];//加上子树上的结点数 } dp[u][son[u]] = min(dp[u][son[u]] , w[u]);//如果选取了u本身,则花费为w(u)}int main(){ char s[200000]; //freopen("in.txt","r",stdin); while(gets(s)){ int i , j , k , c , id = 1 , u , v;//id从1开始,后面将增加一个虚拟结点0,将森林化为树。 if(s[0] == '#') break; sscanf(s , "%d%d" , &n ,&m);//这里s必须为char数组 for(i = 0 ;i <= n;i++) g[i].clear(); h.clear(); memset(in , 0 , sizeof(in)); memset(son , 0 , sizeof(son)); memset(dp , 63 , sizeof(dp)); for(i = 1;i <= n;i++){ scanf("%s",s); if(h.find(s) == h.end()) h[s] = id++; u = h[s]; scanf("%d",&w[u]); gets(s); stringstream str(s); string name; while(str >> name){ if(h.find(name) == h.end()) h[name] = id++; v = h[name]; g[u].push_back(v); in[v]++; } } for(i = 1;i <= n;i++) if(in[i] == 0) g[0].push_back(i); dfs(0); int ans = dp[0][m]; for(i = m;i <= n;i++) ans = min(ans , dp[0][i]); printf("%d\n",ans); } return 0;}
OJ题目:click here~~
给出一棵树上每个结点的权值,找有m个结点的子树的权值和最大值。
const int maxn = 102;int n , m;vector<int> g[maxn];int visit[maxn];int dp[maxn][maxn];void dfs(int u , int father){ int i , j , k; for(i = 0;i < g[u].size();i++){ int v = g[u][i]; if(v == father)continue; dfs(v , u); for(j = m;j > 1;j--){ for(k = 1;k < j;k++){ dp[u][j] = max(dp[u][j] , dp[v][k] + dp[u][j - k]); } } }}int main(){ int i , j , k , a , b; //freopen("in.txt","r",stdin); while(scanf("%d%d",&n,&m) != EOF){ for(i = 0;i < n;i++) g[i].clear(); memset(dp , 0 , sizeof(dp)); for(i = 0 ;i < n;i++) scanf("%d",&dp[i][1]); for(i = 1;i < n;i++){ scanf("%d%d",&a,&b); g[a].push_back(b); g[b].push_back(a); } dfs(0 , -1); int ans = 0; for(i = 0;i < n;i++) ans = max(ans , dp[i][m]); cout << ans << endl; }}
OJ题目:click here~~
树上的01背包,选过父结点才能选自结点。
const int maxn = 102;int val[maxn];int w[maxn];vector<int> g[maxn];int dp[maxn][maxn];int n , m ;void dfs(int u , int father){ int v , i , j , k; for(i = w[u];i <= m;i++) dp[u][i] = val[u]; for(i = 0;i < g[u].size();i++){ v = g[u][i]; if(v == father) continue; dfs(v , u); for(j = m;j >= w[u];j--){ for(k = 1;k + j <= m;k++){ dp[u][j + k] = max(dp[u][j + k] , dp[v][k] + dp[u][j]); } } }}int main(){ int i , j , k ,a , b; //freopen("in.txt" , "r" , stdin); while(scanf("%d%d",&n,&m)){ if(n == -1 && m == -1) break; memset(dp , 0 , sizeof(dp)); for(i = 0 ;i <= n;i++) g[i].clear(); for(i = 1;i <= n;i++){ scanf("%d%d",&w[i] , &val[i]); w[i] = (w[i] + 19)/20;//计算每个结点需要的士兵数 } for(i = 1;i < n;i++){ scanf("%d%d", &a , &b); g[a].push_back(b); g[b].push_back(a); } if(m == 0) {puts("0") ; continue;}//没有士兵时,不能获得任何东西,即使有bug = 0的结点。 dfs(1 , -1); cout << dp[1][m] << endl; }}
0 0