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 201000
10 using namespace std;
11 //map存储标号
12 //边读边记录边的关系
13 //bfs分层记录每层节点
14 //从底层向上层dp
15 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 }

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 201000
10 using namespace std;
11 //map存储标号
12 //边读边记录边的关系
13 //bfs分层记录每层节点
14 //从底层向上层dp
15 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 }

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去更新v
45         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 }

 

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 long
10 #define maxn 51000
11 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 }


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 }


csu 2014 summer training day 3 简单树形dp

时间: 2024-11-08 20:11:20

csu 2014 summer training day 3 简单树形dp的相关文章

csu 2014 summer training day 2 莫比乌斯反演

SPOJ VLATTICE 题意:x,y,z<=1000000,x<=a,y<=b,z<=c,给定a.b.c,求gcd(x,y,z)=1的个数 解释:设 f(n)是gcd(x,y,z)=n的种数,F(n)=n|gcd(x,y,z)的种数 那么F(n)=f(n)+f(2n)....=sigm(f(d)){n|d} 那么根据反演公式 f(n)=sigm(u(d/n)*F(d)){n|d} 我们要求的是f(1)=sigm(u(1)*F(n)+u(2)*F(2n)+u(3)*F(3n)..

2014 Super Training #1 F Passage 概率DP

原题: HDU 3366   http://acm.hdu.edu.cn/showproblem.php?pid=3366 本来用贪心去做,怎么都WA,后来看网上原来是一个DP题. 首先按P/Q来做排序,即P越大,Q越小就越好,这样可以确保先选最优的路走. dp[i][j]表示已经到了第i条路(说明前i-1条都没成功的情况),还剩j块钱时能够走出去的概率. 则方程: dp[i][j] = way[i].P + way[i].Q*(dp[i+1][j-1]) + way[i].D*(dp[i+1]

hdu 1520Anniversary party(简单树形dp)

Anniversary party Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 4310    Accepted Submission(s): 1976 Problem Description There is going to be a party to celebrate the 80-th Anniversary of the

sgu 143 Long live the Queen 简单树形dp

// sgu 143  Long live the Queen 简单树形dp // // 题意:在树上选一个连通字图,使得节点的权值之和最大 // f[i] 表示以该节点为根的字图权值之和的最大值 // 则有 f[i] = w[i] + sigma(max(0,f[j])) i是j的父节点 // 最后在所有的f中挑个最大值就是答案.... #include <algorithm> #include <bitset> #include <cassert> #include

[Luogu P1122]最大子树和 (简单树形DP)

题面 传送门:https://www.luogu.org/problemnew/show/P1122 Solution 这是一道简单的树形DP题. 首先,我们可以转换一下题面,可以发现,题目要求我们求出一颗树上的最大联通子图. 因为我们是在树上取的,实际上就是取一颗子树. 这个就是最基础的树形DP模型了. 我们可以设f[i]表示我们选的子图以i为根所能取的子树的最大值. 转移是: f[i] = beauty[i] + xigema(max(f[j],0)) (也就是一颗树的孩子所能取的子树,如果

HDU 3899 简单树形DP

题意:一棵树,给出每个点的权值和每条边的长度, 点j到点i的代价为点j的权值乘以连接i和j的边的长度.求点x使得所有点到点x的代价最小,输出 虽然还是不太懂树形DP是什么意思,先把代码贴出来把. 这道题目的做法是:先进行一次DFS,以每个节点为根,求出它下面节点到它的数量和. 再进行一次DFS,以每个节点为根,求出它下面节点到它的花费总和. source code: #pragma comment(linker, "/STACK:16777216") //for c++ Compile

UVALive - 2038 Strategic game (无向+记忆化+简单树形DP)

题目链接:https://vjudge.net/problem/UVALive-2038 题意:给定一棵树,选择尽量少的点,使得每个没有选中的结点至少和一个已经选中的结点相邻.输出最少需要选择的节点数.思路:经典的二分图最小顶点覆盖, 也是经典的树形 DP AC代码: #include<stdio.h> #include<string.h> #include<vector> #include<algorithm> using namespace std; c

CSU 1804 - 有向无环图 - [树形DP]

题目链接:http://acm.csu.edu.cn/csuoj/problemset/problem?pid=1804 Bobo 有一个 n 个点,m 条边的有向无环图(即对于任意点 v,不存在从点 v 开始.点 v 结束的路径). 为了方便,点用 1,2,-,n 编号. 设 count(x,y) 表示点 x 到点 y 不同的路径数量(规定 count(x,x)=0),Bobo 想知道 除以 (10 9+7) 的余数. 其中,a i,b j 是给定的数列. Input 输入包含不超过 15 组

poj2342 Anniversary party 简单树形dp

http://poj.org/problem?id=2342 Anniversary party Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 4322   Accepted: 2459 Description There is going to be a party to celebrate the 80-th Anniversary of the Ural State University. The Universi