首页 > 代码库 > csu 2014 summer training day 3 简单树形dp

csu 2014 summer training day 3 简单树形dp

FZU 2157

题意:树上的节点可以打上0或1的标记,树的权值由两部分呢组成,点权和边权,有00、01、10、11四种组合的边权,

问最小权值和。以1节点为树根

分析:dp[x][0]表示x标记0后的最小的权值,dp[x][1]同理

那么每次可以计算dp[x][0],dp[x][1];

例如dp[x][1]=min(dp[son][0]+lab[0][1]+val[1],dp[son][1]+bal[1][1]+val[1]);

具体看代码。

用bfs写的分层,深搜代码更简洁

 1 #include <iostream> 2 #include <string.h> 3 #include <stdio.h> 4 #include <vector> 5 #include <map> 6 #include <string> 7 #include <queue> 8 #define LL long long 9 #define maxn 20100010 using namespace std;11 //map存储标号12 //边读边记录边的关系13 //bfs分层记录每层节点14 //从底层向上层dp15 int la[maxn][2];16 int bel[maxn][2][2];17 vector<int> G[maxn];18 vector<int> Ceil[maxn];19 int n,cnum;//点数,分层数20 void init(){21     for(int i=0;i<maxn;i++) G[i].clear();22     for(int i=0;i<maxn;i++) Ceil[i].clear();23     cnum=0;24     return ;25 }26 typedef pair<int,int>PAIR;27 bool vis[maxn];28 void Bfs(){29     queue<PAIR> Q ;30     Q.push(make_pair(1,1));31     memset(vis,0,sizeof(vis));32     vis[1]=true;33     while(!Q.empty()){34         PAIR New=Q.front();35         Q.pop();36         int u=New.first;37         int st=New.second;38         Ceil[st].push_back(u);39         cnum=max(cnum,st);40         for(int i=0;i<G[u].size();i++){41             int v=G[u][i];42             if (vis[v]) continue;43             Q.push(make_pair(v,st+1));44             vis[v]=true;45         }46     }47     return;48 }49 LL dp[maxn][2];//0表示不选择i节点,1表示选择i节点50 void solve(){51     memset(dp,0,sizeof(dp));52     for(int i=0;i<Ceil[cnum].size();i++){53         int u=Ceil[cnum][i];54         dp[u][0]=(LL)la[u][0];dp[u][1]=(LL)la[u][1];55     }56     for(int i=cnum-1;i>=1;i--){57         for(int j=0;j<Ceil[i].size();j++){58             int u=Ceil[i][j];59             for(int k=0;k<G[u].size();k++){60                 int v=G[u][k];61                 dp[u][0]+=min(dp[v][0]+bel[v][0][0],dp[v][1]+bel[v][0][1]);62                 dp[u][1]+=min(dp[v][0]+bel[v][1][0],dp[v][1]+bel[v][1][1]);63             }64             dp[u][1]+=(LL)la[u][1];65             dp[u][0]+=(LL)la[u][0];66 //            cout<<"u="<<u<<","<<"1:"<<dp[u][1]<<endl;67 //            cout<<"u="<<u<<","<<"0:"<<dp[u][0]<<endl;68         }69     }70     printf("%I64d\n",min(dp[1][1],dp[1][0]));71     return ;72 }73 int t;74 int main(){75     scanf("%d",&t);76     while(t--){77         scanf("%d",&n);78         init();79         for(int i=1;i<=n;i++){80             scanf("%d",&la[i][0]);81         }82         for(int i=1;i<=n;i++){83             scanf("%d",&la[i][1]);84         }85         int u,v,a,b,c,d;86         for(int i=1;i<=n-1;i++){87             scanf("%d%d%d%d%d%d",&u,&v,&a,&b,&c,&d);88             bel[v][0][0]=a;89             bel[v][0][1]=b;90             bel[v][1][0]=c;91             bel[v][1][1]=d;92             G[u].push_back(v);93         }94         Bfs();//分层95         solve();96     }97     return 0;98 }
View Code

 

POJ 2342

题意:给定一颗boss树,父亲节点和孩子节点不能同时出现,问最多能选出多少节点

分析:dp[x][0]:x不出现,dp[x][1]:x出现

dp[u][0]+=max(dp[v][1],dp[v][0]);dp[u][1]+=dp[v][0];
 1 #include <iostream> 2 #include <string.h> 3 #include <stdio.h> 4 #include <vector> 5 #include <map> 6 #include <string> 7 #include <queue> 8 #define LL long long 9 #define maxn 20100010 using namespace std;11 //map存储标号12 //边读边记录边的关系13 //bfs分层记录每层节点14 //从底层向上层dp15 int la[maxn][2];16 int bel[maxn][2][2];17 vector<int> G[maxn];18 vector<int> Ceil[maxn];19 int n,cnum;//点数,分层数20 void init(){21     for(int i=0;i<maxn;i++) G[i].clear();22     for(int i=0;i<maxn;i++) Ceil[i].clear();23     cnum=0;24     return ;25 }26 typedef pair<int,int>PAIR;27 bool vis[maxn];28 void Bfs(){29     queue<PAIR> Q ;30     Q.push(make_pair(1,1));31     memset(vis,0,sizeof(vis));32     vis[1]=true;33     while(!Q.empty()){34         PAIR New=Q.front();35         Q.pop();36         int u=New.first;37         int st=New.second;38         Ceil[st].push_back(u);39         cnum=max(cnum,st);40         for(int i=0;i<G[u].size();i++){41             int v=G[u][i];42             if (vis[v]) continue;43             Q.push(make_pair(v,st+1));44             vis[v]=true;45         }46     }47     return;48 }49 LL dp[maxn][2];//0表示不选择i节点,1表示选择i节点50 void solve(){51     memset(dp,0,sizeof(dp));52     for(int i=0;i<Ceil[cnum].size();i++){53         int u=Ceil[cnum][i];54         dp[u][0]=(LL)la[u][0];dp[u][1]=(LL)la[u][1];55     }56     for(int i=cnum-1;i>=1;i--){57         for(int j=0;j<Ceil[i].size();j++){58             int u=Ceil[i][j];59             for(int k=0;k<G[u].size();k++){60                 int v=G[u][k];61                 dp[u][0]+=min(dp[v][0]+bel[v][0][0],dp[v][1]+bel[v][0][1]);62                 dp[u][1]+=min(dp[v][0]+bel[v][1][0],dp[v][1]+bel[v][1][1]);63             }64             dp[u][1]+=(LL)la[u][1];65             dp[u][0]+=(LL)la[u][0];66 //            cout<<"u="<<u<<","<<"1:"<<dp[u][1]<<endl;67 //            cout<<"u="<<u<<","<<"0:"<<dp[u][0]<<endl;68         }69     }70     printf("%I64d\n",min(dp[1][1],dp[1][0]));71     return ;72 }73 int t;74 int main(){75     scanf("%d",&t);76     while(t--){77         scanf("%d",&n);78         init();79         for(int i=1;i<=n;i++){80             scanf("%d",&la[i][0]);81         }82         for(int i=1;i<=n;i++){83             scanf("%d",&la[i][1]);84         }85         int u,v,a,b,c,d;86         for(int i=1;i<=n-1;i++){87             scanf("%d%d%d%d%d%d",&u,&v,&a,&b,&c,&d);88             bel[v][0][0]=a;89             bel[v][0][1]=b;90             bel[v][1][0]=c;91             bel[v][1][1]=d;92             G[u].push_back(v);93         }94         Bfs();//分层95         solve();96     }97     return 0;98 }
View Code

 

HDU 2196

题意:给定一棵树,分别求1..N分别作为树根时,最长的树枝长是多少?

分析:两遍dfs,第一遍处理出(以1为根节点):

int max1[maxn];//以1为根,i节点的最长路径int max2[maxn];//以1为根,i节点的次长路径,保证最长边的下一点不在这条路径上

记录下相应的下一个节点的编号:

int max1id[maxn];int max2id[maxn];

第二遍处理出:转移节点后每个点作为根节点点的max1和max2,dfs搜索更新,这样从1号节点开始,每次转移,就能求出每个节点的情况了。
自己注意:向最长路径上的点转移时,长度是不会加一的。

具体看代码:

 1 #include <iostream> 2 #include <stdio.h> 3 #include <string.h> 4 #include <vector> 5 #define maxn 10100 6 using namespace std; 7  8 vector<int>G[maxn]; 9 vector<int>len[maxn];10 int N;11 int max1[maxn];//以1为根,i节点的最长路径12 int max2[maxn];//以1为根,i节点的次长路径,保证最长边的下一点不在这条路径上13 int max1id[maxn];14 int max2id[maxn];15 void init(){16     for(int i=0;i<=N;i++){17         G[i].clear();18         len[i].clear();19     }20 }21 bool vis[maxn];22 void dfs1(int u,int fa){23     max1[u]=0;24     max2[u]=0;25     for(int i=0;i<G[u].size();i++){26         int v=G[u][i];27         if (v==fa) continue;28         dfs1(v,u);29         if (max1[v]+len[u][i]>max2[u]){//这个方法真机智啊30             max2[u]=max1[v]+len[u][i];31             max2id[u]=v;32         }33         if (max1[u]<max2[u]){34             swap(max1[u],max2[u]);35             swap(max1id[u],max2id[u]);36         }37     }38     return;39 }40 void dfs2(int u,int fa){41     for(int i=0;i<G[u].size();i++){42         int v=G[u][i];43         if (v==fa) continue;44         //用u去更新v45         if (max1id[u]==v){46             if (max2[v]<max2[u]+len[u][i]){47                 max2[v]=max2[u]+len[u][i];48                 max2id[v]=u;49             }50         }else {51             if (max2[v]<max1[u]+len[u][i]){52                 max2[v]=max1[u]+len[u][i];53                 max2id[v]=u;54             }55         }56         if (max2[v]>max1[v]){57             swap(max2[v],max1[v]);58             swap(max2id[v],max1id[v]);59         }60         dfs2(v,u);61     }62     return ;63 }64 int main()65 {66     while(~scanf("%d",&N)){67         init();68         int v,c;69         for(int i=2;i<=N;i++){70             scanf("%d%d",&v,&c);71             G[i].push_back(v);72             G[v].push_back(i);73             len[i].push_back(c);74             len[v].push_back(c);75         }76         dfs1(1,-1);77         dfs2(1,-1);78         for(int i=1;i<=N;i++) printf("%d\n",max1[i]);79     }80 81     return 0;82 }
View Code
 

POJ 4045

 

题意:这个题大致意思是给你一颗树,让你求一点,使该点到其余各点的距离之和最小。如果这样的点有多个,则按升序依次输出。

分析:cunt[x]记录以x为根的子树中节点的个数

        dp[x]记录以x为根时,x到所有节点的距离之和

        第一遍dfs处理这两个

        又是一道根节点转移的题目

vsum=sum-cunt[v]-1+(N-cunt[v]-2)+1;
sum是以当前节点u为根时的所求值,vsum是将根转移到v节点上,所以v节点形成的子树整体上移,深度都减少了1,而其他节点深度都增加了1,这个在纸上画一画就明白了。
所以dfs就能搞定了

 1 #include <iostream> 2 #include <string.h> 3 #include <stdio.h> 4 #include <vector> 5 #include <algorithm> 6 #include <map> 7 #include <string> 8 #include <queue> 9 #define LL long long10 #define maxn 5100011 using namespace std;12 vector<int>G[maxn];13 LL dp[maxn];14 LL cunt[maxn];15 bool vis[maxn];16 LL Min;17 int cnt=0;18 int ans[maxn];19 void init(){20     for(int i=0;i<maxn;i++) G[i].clear();21     memset(dp,0,sizeof(dp));22     memset(cunt,0,sizeof(cunt));23 }24 LL dfs1(int u){25     LL ans=0;26     for(int i=0;i<G[u].size();i++){27         int v=G[u][i];28         if (vis[v]) continue;29         vis[v]=true;30         ans+=dfs1(v)+1;31     }32     return cunt[u]=ans;33 }34 LL dfs2(int u){35     LL ans=0;36     for(int i=0;i<G[u].size();i++){37         int v=G[u][i];38         if (vis[v]) continue;39         vis[v]=true;40         ans+=dfs2(v)+1+cunt[v];41     }42     return dp[u]=ans;43 }44 LL sum1,cunt1;45 LL N,I,R;46 void dfs3(int u,LL sum){47     LL vsum=0;48     for(int i=0;i<G[u].size();i++){49         int v=G[u][i];50         if (vis[v]) continue;51         vsum=sum-cunt[v]-1+(N-cunt[v]-2)+1;52         if (vsum==Min) ans[cnt++]=v;53         if (vsum<Min) {54             cnt=0;55             ans[cnt++]=v;56             Min=vsum;57         }58         LL cu=cunt[u],cv=cunt[v];59         cunt[v]=N-1;60         cunt[u]=N-cv-2;61         vis[v]=true;62         dfs3(v,vsum);63         cunt[v]=cv,cunt[u]=cu;64     }65     return ;66 }67 int t;68 int main(){69     scanf("%d",&t);70     while(t--){71         init();72         scanf("%lld%lld%lld",&N,&I,&R);73         for(int i=1;i<=N-1;i++){74             int u,v;75             scanf("%d%d",&u,&v);76             G[u].push_back(v);77             G[v].push_back(u);78         }79         memset(vis,0,sizeof(vis));vis[1]=true;80         cunt1=dfs1(1);81         memset(vis,0,sizeof(vis));vis[1]=true;82         sum1=dfs2(1);83         Min=sum1;cnt=0;84         ans[cnt++]=1;85         memset(vis,0,sizeof(vis));vis[1]=true;86         dfs3(1,sum1);87         sort(ans,ans+cnt);88         printf("%lld\n",Min*I*I*R);89         for(int i=0;i<cnt;i++){90             if (i==cnt-1) printf("%d\n",ans[i]);91             else printf("%d ",ans[i]);92         }93         printf("\n");94     }95     return 0;96 }
View Code

POJ 3342

题意:和第二题很想,父亲和孩子不能都选择,但是要判断选择的方案是否唯一

分析:讨论一下新增部分,

新增dup[x][0]、dup[x][1]分别表示选与不选是否唯一

memset(dup,0,sizeof(dup));//1表示确定,0不确定
其中v就是son节点
if ((dp[v][0]>dp[v][1]&&dup[v][0]==0)||(dup[v][1]>dup[v][0]&&dup[v][1]==0)||(dp[v][1]==dp[v][0]))                    dup[u][0]=0;if (dup[v][0]==0) dup[u][1]=0;
我们判断dp[v][0]和dp[v][1]哪个大,这样dup[v][0]的状态就延续上去了(当然是必然选择的那个状态延续),当然若dp[v][1]==dp[v][0],dup就是0了
dup需要赋初值1已做标记
  1 #include <iostream>  2 #include <string.h>  3 #include <stdio.h>  4 #include <vector>  5 #include <map>  6 #include <string>  7 #include <queue>  8 #define maxn 310  9 using namespace std; 10 //map存储标号 11 //边读边记录边的关系 12 //bfs分层记录每层节点 13 //从底层向上层dp 14 vector<int> G[maxn]; 15 vector<int> Ceil[maxn]; 16 map<string,int>Mp; 17 int n,cnum,cnt;//人数,分层数 18 void init(){ 19     for(int i=0;i<maxn;i++) G[i].clear(); 20     for(int i=0;i<maxn;i++) Ceil[i].clear(); 21     cnum=0;cnt=0; 22     Mp.clear(); 23     return ; 24 } 25 typedef pair<int,int>PAIR; 26 bool vis[maxn]; 27 void Bfs(){ 28     queue<PAIR> Q ; 29     Q.push(make_pair(1,1)); 30     memset(vis,0,sizeof(vis)); 31     vis[1]=true; 32     while(!Q.empty()){ 33         PAIR New=Q.front(); 34         Q.pop(); 35         int u=New.first; 36         int st=New.second; 37         Ceil[st].push_back(u); 38         cnum=max(cnum,st); 39         for(int i=0;i<G[u].size();i++){ 40             int v=G[u][i]; 41             if (vis[v]) continue; 42             Q.push(make_pair(v,st+1)); 43             vis[v]=true; 44         } 45     } 46     return; 47 } 48 int dp[maxn][2];//0表示不选择i节点,1表示选择i节点 49 int dup[maxn][2]; 50 void solve(){ 51     memset(dup,0,sizeof(dup));//1表示确定,0不确定 52     memset(dp,0,sizeof(dp)); 53     for(int i=0;i<Ceil[cnum].size();i++){ 54         int u=Ceil[cnum][i]; 55         dp[u][0]=0;dp[u][1]=1; 56         dup[u][0]=dup[u][1]=1; 57     } 58     for(int i=cnum-1;i>=1;i--){ 59         for(int j=0;j<Ceil[i].size();j++){ 60             int u=Ceil[i][j]; 61             dup[u][0]=dup[u][1]=1; 62             for(int k=0;k<G[u].size();k++){ 63                 int v=G[u][k]; 64                 dp[u][0]+=max(dp[v][0],dp[v][1]); 65                 dp[u][1]+=dp[v][0]; 66                 if ((dp[v][0]>dp[v][1]&&dup[v][0]==0)||(dup[v][1]>dup[v][0]&&dup[v][1]==0)||(dp[v][1]==dp[v][0])) 67                     dup[u][0]=0; 68                 if (dup[v][0]==0) dup[u][1]=0; 69             } 70             dp[u][1]++; 71         } 72     } 73     if (dp[1][0]==dp[1][1]){ 74         printf("%d No\n",dp[1][0]); 75     } 76     if (dp[1][0]>dp[1][1]){ 77         printf("%d ",dp[1][0]); 78         if (dup[1][0]) puts("Yes");else puts("No"); 79     } 80     if (dp[1][1]>dp[1][0]){ 81         printf("%d ",dp[1][1]); 82         if (dup[1][1]) puts("Yes");else puts("No"); 83     } 84     return; 85 } 86 int main(){ 87     string s; 88     char s1[55],s2[55]; 89     while(~scanf("%d",&n)&&n){ 90         init(); 91         scanf("%s",s1); 92         Mp[s=s1]=++cnt; 93         for(int i=1;i<n;i++){ 94             scanf("%s",s1);scanf("%s",s2); 95             int u,v; 96             if (Mp.count(s=s1)) u=Mp[s=s1]; 97             else u=Mp[s=s1]=++cnt; 98             if (Mp.count(s=s2)) v=Mp[s=s2]; 99             else v=Mp[s=s2]=++cnt;100             G[v].push_back(u);101         }102         Bfs();//分层103         solve();104     }105     return 0;106 }
View Code