树形DP

树形DP小结:

              1:n个点,n-1条边;

              2:无环,根结点可由子结点推出;

              3:一般的状态都是以某一个结点为根的子树代表什么,

                     然后通过递归,由一个一个子结点推出根结点状态;

                     递推关系:Dp[i][j]=max(dp[i][j],dp[i][k]+dp[son[i][j-k]);

                     说明递推的含义,首先dp[i][j]的第一个值肯定从dp[son[i]][k]来,当换另一个son2[i],dp[i][j]就是由俩个son推出,其他son同理;

              4:对于一棵树,可以随意选一个点为根,开一个vis[]数组,记录该结点是否访问过;

                    

              http://acm.hdu.edu.cn/showproblem.php?pid=1520

              最裸的树形DP, 定义dp[i][0]表示以i为根的子树不选i能邀请到的最大值;

              Dp[i][1]表示以i为根的子树选i能邀请到的最大值;

              状态转移:dp[i][0]+=max(dp[son][0],dp[son][1]);   dp[i][1]+=dp[son][0];

              注意每个点的值有正有负;

    

View Code
 1 #include<cstdio>
 2 #include<cstring>
 3 #include<iostream>
 4 #include<algorithm>
 5 #include<cstdlib>
 6 #include<cmath>
 7 #include<vector>
 8 using namespace std;
 9 const int MAXN=6010;
10 int dp[MAXN][2];
11 const int INF=1000000; 
12 vector <int > g[MAXN];
13 int c[MAXN];
14 int n;
15 int v[MAXN]; 
16 void TREEDP(int root)
17 {
18         int d=g[root].size();
19         if (d==0) {
20                    dp[root][1]=c[root];
21                    dp[root][0]=0;
22                   return ;                 
23          } 
24         for (int i=0;i<d;i++)
25         {
26             int c=g[root][i]; 
27              TREEDP(c); 
28         } 
29          
30         dp[root][1]=c[root];
31         for (int i=0;i<d;i++)
32         {
33              int c=g[root][i];
34              if (dp[c][0]>0) 
35               dp[root][1]+=dp[c][0]; 
36         } 
37         dp[root][0]=-INF;
38         for (int i=0;i<d;i++)
39         {
40               int c=g[root][i];
41               int t=max(dp[c][1],dp[c][0]); 
42               if (t>0  && dp[root][0]<0)
43              {
44                    dp[root][0]=t;
45                 }      else if (t<0 && dp[root][0]<0)
46              {
47                    if (dp[root][0]<t) dp[root][0]=t;   
48              }     else if (t>0  && dp[root][0]>=0)
49              {
50                    dp[root][0]+=t; 
51              }  
52         } 
53 } 
54 int main()
55 {
56    // freopen("D:\in.txt","r",stdin);    
57     while (~scanf("%d",&n))
58     {
59            for (int i=1;i<=n;i++)
60          {
61              scanf("%d",&c[i]); 
62              g[i].clear(); 
63          } 
64          int u,v;
65          bool  vis[MAXN];
66          memset(vis,0,sizeof(vis)); 
67          while (~scanf("%d%d",&u,&v)) 
68            {
69                  if (u==0 && v==0) break; 
70                 g[v].push_back(u);  
71                   vis[u]=1; 
72          } 
73          memset(dp,0,sizeof(dp)); 
74          
75          for (int i=1;i<=n;i++)
76          {
77                   if (vis[i]==0)
78                 {
79                     TREEDP(i);
80                     cout<<max(dp[i][0],dp[i][1])<<endl; 
81                     break; 
82                 } 
83          } 
84          
85          
86     } 
87 
88     return 0;
89 }

             

              http://acm.hdu.edu.cn/showproblem.php?pid=2412

              和上一题基本一样,但还要询问方案是否唯一;加一个数组记录当前状态是否唯一如果不唯一,当根结点的状态有它推出时,根结点的状态也就不唯一

                //程序统一输出的方式,不要cout和printf混用,俩着输出性质不一样;

          

View Code
  1 #include<cstdio>
  2 #include<cstring>
  3 #include<cstdlib>
  4 #include<iostream>
  5 #include<algorithm>
  6 #include<map>
  7 #include<vector> 
  8 #include<string> 
  9 using namespace std;
 10 const int MAXN=210;
 11 map<string,int > mp;
 12 int sz; 
 13 vector <int >g[MAXN]; 
 14 int dp[MAXN][2];
 15 int dug[MAXN][2]; 
 16 int vis[MAXN];
 17 int n; 
 18 void init()
 19 {
 20      sz=0;
 21     mp.clear();
 22     for (int i=0;i<n;i++) g[i].clear(); 
 23      memset(vis,0,sizeof(vis)); 
 24 } 
 25 void TREEDP(int rt)
 26 {
 27           if (vis[rt]) return ;
 28         vis[rt]=1; 
 29         int d=g[rt].size();
 30         if (d==0)
 31         {
 32                 dp[rt][0]=0;
 33                 dp[rt][1]=1;
 34                 dug[rt][0]=1;
 35                 dug[rt][1]=1; 
 36                 return; 
 37         }
 38         dp[rt][0]=dp[rt][1]=0; 
 39         dug[rt][0]=dug[rt][1]=1; 
 40         for (int i=0;i<d;i++)
 41         {
 42                 int c=g[rt][i];
 43                 if (!vis[c])
 44                 {
 45                        TREEDP(c);
 46                 }        
 47                 dp[rt][1]+=dp[c][0];
 48                 if (dug[c][0]==0) dug[rt][1]=0;  
 49                 dp[rt][0]+=max(dp[c][1],dp[c][0]);
 50                 if (dp[c][1]>dp[c][0] && dug[c][1]==0)
 51                 {
 52                         dug[rt][0]=0; 
 53                 } else
 54                 if (dp[c][1]<dp[c][0] && dug[c][0]==0)
 55                 {
 56                         dug[rt][0]=0; 
 57                 } else if (dp[c][1]==dp[c][0]) dug[rt][0]=0; 
 58         } 
 59         dp[rt][1]+=1; 
 60          
 61 
 62 } 
 63 int mp_find(string s)
 64 {
 65     if (mp.find(s)==mp.end()) 
 66     {
 67             mp[s]=sz++; 
 68     } 
 69     return mp[s]; 
 70 } 
 71 int main()
 72 {
 73     //freopen("D:\in.txt","r",stdin); 
 74     string s,s1,s2; 
 75     while (cin>>n)
 76     {
 77             if (n==0) break;
 78             init(); 
 79             cin>>s; 
 80              mp_find(s); 
 81              for (int i=0;i<n-1;i++)
 82             {
 83                  cin>>s1>>s2;
 84                 int u=mp_find(s1);
 85                 int v=mp_find(s2);
 86                 g[v].push_back(u); 
 87             } 
 88             
 89             TREEDP(0);
 90             
 91             if (dp[0][1]>dp[0][0])
 92             {
 93                  if(dug[0][1]) cout<<dp[0][1]<<" Yes"<<endl;//printf("%d Yes\n",dp[0][1]);
 94                 else cout<<dp[0][1]<<" No"<<endl;//printf("%d No\n",dp[0][1]); 
 95             } else if (dp[0][1]<dp[0][0]) 
 96             {
 97                  if(dug[0][0])cout<<dp[0][0]<<" Yes"<<endl;// printf("%d Yes\n",dp[0][0]);
 98                 else cout<<dp[0][0]<<" No"<<endl;//printf("%d No\n",dp[0][0]); 
 99             } else 
100             {
101                 cout<<dp[0][1]<<" No"<<endl;//printf("%d No\n",dp[0][1]); 
102             
103             } 
104             
105     
106     }        
107 
108 
109     return 0; 
110 } 

   

              http://acm.hdu.edu.cn/showproblem.php?pid=3899

              很裸的树形DP,先一遍dfs,统计出以每个结点为根的子树包含的队伍,和该子树以该子树跟为聚集地所走的路径,这样就算出了以根结点为聚集地需要的总路程;

              再一遍   dfs,由总根结点推到子结点,记录下最小值。

              //对于数据大的,但有感觉没有超,还是果断用long long ,有可能中间计算超了;

              //该题用dfs递归会爆栈,要手写stack,或用C++交可以用爆栈外挂,

               #pragma comment(linker, "/STACK:1024000000,1024000000")

View Code
  1 #pragma comment(linker, "/STACK:1024000000,1024000000")
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<iostream>
  5 #include<algorithm>
  6 #include<cstdlib>
  7 #include<cmath>
  8 #include<vector>
  9 using namespace std;
 10 const int N=100100;
 11 typedef __int64 ll;
 12 ll dp[N];
 13 ll dug[N];
 14 int n;
 15 ll c[N],vis[N];
 16 ll ans;
 17 struct node
 18 {
 19         int v,w;
 20 };
 21 vector <node> g[N];
 22 void TREEDP(int rt)
 23 {
 24         vis[rt]=1;
 25         int d=g[rt].size();
 26         if (d==0)
 27         {
 28                 dp[rt]=c[rt];
 29                 dug[rt]=0;
 30                 return;
 31         }
 32         dp[rt]=c[rt];
 33         dug[rt]=0;
 34         for (int i=0;i<d;i++)
 35         {
 36                 int c=g[rt][i].v;
 37                 int w=g[rt][i].w;
 38                 if (vis[c]) continue;
 39                 TREEDP(c);
 40                 dp[rt]+=dp[c];
 41                 dug[rt]+=dp[c]*w+dug[c];    
 42         }
 43 }
 44 void init()
 45 {
 46         memset(dp,0,sizeof(dp));
 47         for (int i=1;i<=n;i++) g[i].clear();
 48           memset(vis,0,sizeof(vis));
 49           memset(dug,0,sizeof(dug));
 50 }
 51 void solve(int rt)
 52 {
 53         vis[rt]=1;
 54         int d=g[rt].size();
 55         for (int i=0;i<d;i++)
 56         {
 57                 int c=g[rt][i].v;
 58                 if (vis[c]) continue;
 59                 int w=g[rt][i].w;
 60                 ll t1=dp[c],t2=dug[c];
 61                 ll t3=dp[rt],t4=dug[rt];
 62                 
 63                 dp[c]=t3;
 64                 dp[rt]=t3-t1;
 65                 dug[rt]=t4-t1*w-t2;
 66                 dug[c]=t4-w*t1+(t3-t1)*w;
 67                 ans=min(dug[c],ans);    
 68                 solve(c);
 69                 dp[c]=t1;dug[c]=t2;
 70                 dp[rt]=t3;dug[rt]=t4;            
 71         }
 72           vis[rt]=0;
 73 
 74 }
 75 int main()
 76 {    
 77         //freopen("D:\in.txt","r",stdin);
 78         while (~scanf("%d",&n))
 79         {
 80                 for (int i=1;i<=n;i++)
 81                 {
 82                         scanf("%I64d",&c[i]); 
 83                 }
 84                 init(); 
 85                 for (int i=0;i<n-1;i++)
 86                 {
 87                         int u,v,w;
 88                         scanf("%d%d%d",&u,&v,&w);
 89                         node t1={v,w};
 90                         g[u].push_back(t1);
 91                           node t2={u,w};
 92                           g[v].push_back(t2);
 93                 }
 94                 TREEDP(1);
 95                 ans=dug[1];
 96                 //cout<<ans<<endl;
 97                 memset(vis,0,sizeof(vis));
 98                 solve(1); 
 99                 printf("%I64d\n",ans);
100         
101         } 
102 
103         return 0;
104 }

              http://acm.hdu.edu.cn/showproblem.php?pid=2196

              求树上离一个点最远的距离;我是先一遍dfs记录下以该点为根的子树的子结点到根的最远距离,然后转移总根结点,求出每个结点的ans;同上类似;

               

View Code
  1     
  2 #include<cstdio>
  3 #include<iostream>
  4 #include<cstring>
  5 #include<algorithm>
  6 #include<cstdlib>
  7 #include<cmath>
  8 #include<vector>
  9 using namespace std;
 10 typedef long long ll;
 11 const int N=10010;
 12 ll dp[N];
 13 struct node
 14 {
 15         int v,w;
 16 };
 17 vector<node> g[N];
 18 int vis[N];
 19 ll c[N];
 20 int n;
 21 void init()
 22 {
 23         for (int i=1;i<=n;i++)
 24         g[i].clear();
 25         memset(dp,0,sizeof(dp));
 26         memset(vis,0,sizeof(vis));
 27 }
 28 void TREEDP(int rt)
 29 {
 30         vis[rt]=1;
 31         int d=g[rt].size();
 32         if (d==0)
 33         {
 34                 dp[rt]=0;
 35                 return;
 36         }
 37         for (int i=0;i<d;i++)
 38         {
 39                 ll v=g[rt][i].v;
 40                 ll w=g[rt][i].w;
 41                 if (vis[v]) continue;
 42                 TREEDP(v);
 43                 dp[rt]=max(dp[rt],dp[v]+w);
 44         } 
 45 
 46 }
 47 ll find(int rt,int x)
 48 {
 49         ll t=0;
 50         int d=g[rt].size();
 51         for (int i=0;i<d;i++)
 52         {
 53                 int v=g[rt][i].v;
 54                 int w=g[rt][i].w;
 55                 if(x==v) continue;
 56                 t=max(t,dp[v]+w);
 57         }
 58           return t;
 59 }
 60 void solve(int rt)
 61 {
 62         vis[rt]=1;
 63         int d=g[rt].size();
 64         if (d==0)
 65         {
 66                 c[rt]=dp[rt];
 67                 return;
 68         }        
 69         for (int i=0;i<d;i++)
 70         {
 71                 int v=g[rt][i].v;
 72                 if (vis[v]) continue;
 73                 ll w=g[rt][i].w;
 74                 ll t=dp[v],t3=dp[rt];
 75                 ll t2=find(rt,v)+w;
 76         
 77                  
 78                 dp[v]=max(dp[v],t2);
 79                 dp[rt]=t2-w;
 80                 c[v]=dp[v];
 81                 solve(v);
 82                 dp[rt]=t3;dp[v]=t;
 83                 
 84         }
 85 
 86 }
 87 int main()
 88 {
 89         //freopen("D:\in.txt","r",stdin);
 90         while (~scanf("%d",&n))
 91         {
 92                  init();
 93                 for (int i=2;i<=n;i++)
 94                 {
 95                         int v,w;
 96                         scanf("%d%d",&v,&w);
 97                         node t1={v,w};
 98                         g[i].push_back(t1);
 99                         node t2={i,w};
100                         g[v].push_back(t2);
101                 }
102 
103                 TREEDP(1);
104                 c[1]=dp[1];
105                 memset(vis,0,sizeof(vis));
106                 solve(1);    
107                 for (int i=1;i<=n;i++)
108                 printf("%d\n",c[i]);    
109         
110         }
111         return 0;
112 }

                http://acm.hdu.edu.cn/showproblem.php?pid=3586 

                二分+树形DP;

                据说凡是求最大最小值或最小最大值都可以二分确定一个因素,然后验证;

                二分上限,树形DP求不超上界切断联络的最小代价;

View Code
  1 #include<cstdio>
  2 #include<cstring>
  3 #include<cstdlib>
  4 #include<iostream>
  5 #include<algorithm>
  6 #include<cmath>
  7 #include<vector>
  8 using namespace std;
  9 const int N=1010;
 10 int dp[N];
 11 int n,all;
 12 int vis[N];
 13 bool bo[N];
 14 struct node
 15 {
 16         int v,w;
 17 };
 18 vector<node> g[N];
 19 
 20 const int INF=1000000000;
 21 void TREEDP(int rt,int c,int m)
 22 {
 23         vis[rt]=1;
 24         int d=g[rt].size();
 25     
 26         int t=0;
 27         int flag=0;
 28         for (int i=0;i<d;i++)
 29         {
 30                 int v=g[rt][i].v;
 31                 int w=g[rt][i].w;
 32                 if (vis[v]) continue;
 33                 TREEDP(v,w,m);
 34                 if (bo[v]) 
 35                 {
 36                         bo[rt]=1;
 37                         break;
 38                 }
 39                 t+=dp[v];
 40                 flag=1;
 41         }
 42         if (bo[rt]==1)
 43         {
 44                 if (c<=m)
 45                 {
 46                         bo[rt]=0;
 47                         dp[rt]=c;
 48                 }
 49         } else 
 50         {
 51                 if (c<=m)
 52                 {
 53                         if (flag)
 54                         dp[rt]=min(t,c);
 55                         else dp[rt]=c;
 56                 }else     
 57                 {        
 58                         if (flag)
 59                         dp[rt]=t;
 60                         else bo[rt]=1;
 61                         
 62                 }
 63         }
 64         
 65 }
 66 bool solve(int m)
 67 {
 68         memset(dp,0,sizeof(dp));
 69         memset(vis,0,sizeof(vis));
 70         memset(bo,0,sizeof(bo));
 71         TREEDP(1,INF,m);
 72         if (bo[1]==1)  return 0;
 73         if(dp[1]<=all) return 1;
 74         return 0;        
 75 }
 76 void init()
 77 {
 78         for (int i=1;i<=n;i++)
 79         g[i].clear();
 80 }
 81 int main()
 82 {
 83         //freopen("D:\in.txt","r",stdin);
 84         while (~scanf("%d%d",&n,&all))
 85         {
 86                 if (n==0 && all==0) break;
 87                 init();
 88                 int l=0,r=1000;
 89                 for (int i=2;i<=n;i++)
 90                 {
 91                         int u,v,w;
 92                         scanf("%d%d%d",&u,&v,&w);
 93                     //    r=max(r,w);
 94                         
 95                         node t1={v,w};
 96                         g[u].push_back(t1);
 97                         node t2={u,w};
 98                         g[v].push_back(t2);
 99                 }
100                 int ans=-1;
101                 while (r>l)
102                 {
103                         int m=(l+r)/2;
104                         if (solve(m))
105                         {
106                                 ans=m;
107                                 r=m;
108                         }else     l=m+1;
109                 }
110                 printf("%d\n",ans);
111                 
112                 
113         }
114         return 0;
115 }

   

              http://acm.hdu.edu.cn/showproblem.php?pid=1561

                     dp[i][j]表示以i为根,攻下j个城市的最大获得;

                     dp[i][j]=max(dp[i][j[,dp[son][k]+dp[i][j-k]);类似于在树上做分组背包;

                    

                    

View Code
 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #include<iostream>
 5 #include<cstdlib>
 6 #include<cmath>
 7 #include<vector> 
 8 using namespace std;
 9 const int MAXN=210;
10 int n,m;
11 vector<int > g[MAXN];
12 int dp[MAXN][MAXN]; 
13 //dp[i][j] ,表示以i为根,攻下j个城市的最大获得; 
14 int c[MAXN]; 
15 void init()
16 {
17                    memset(dp,0,sizeof(dp));
18                 for (int i=0;i<=n;i++)
19                 {
20                         g[i].clear(); 
21                 } 
22 
23 } 
24 void TREEDP(int rt,int m)
25 {
26         
27         int d=g[rt].size();    
28         dp[rt][1]=c[rt];    
29         for (int i=0;i<d;i++)
30         {
31                   
32                 int c=g[rt][i];
33                 if (m>1) 
34                 TREEDP(c,m-1);
35                 for (int j=m;j>0;j--)
36                 {
37                          int v=j+1;
38                         for (int k=1;k<v;k++) 
39                         if (dp[rt][v]<dp[rt][v-k]+dp[c][k])
40                         {
41                                 dp[rt][v]=dp[rt][v-k]+dp[c][k]; 
42                         } 
43                 
44                 } 
45         } 
46 
47 } 
48 int main()
49 {
50     //    freopen("D:\in.txt","r",stdin); 
51         while (~scanf("%d%d",&n,&m)) 
52         {
53                 if (n==0 && m==0) break; 
54                 c[0]=0; init(); 
55                 for (int i=1;i<=n;i++)
56                 {
57                         int v; 
58                         scanf("%d%d",&v,&c[i]);
59                         g[v].push_back(i); 
60                 }
61                 TREEDP(0,m+1);        
62                 printf("%d\n",dp[0][m+1]); 
63         
64         }
65         return 0; 
66 } 

                     http://acm.hdu.edu.cn/showproblem.php?pid=1011    

                     同上;注意特判;

View Code
 1 #include<cstdio>
 2 #include<cstring>
 3 #include<cstdlib>
 4 #include<algorithm>
 5 #include<iostream>
 6 #include<cmath>
 7 #include<vector>
 8 using namespace std;
 9 
10 const int MAXN=110;
11 int n,m;
12 vector<int > g[MAXN];
13 int c[MAXN],w[MAXN];
14 int dp[MAXN][MAXN];
15 int vis[MAXN]; 
16 //dp[i][j] 以i为根花费j能获得的最大利益; 
17 
18 void TREEDP(int rt)
19 {
20     
21         vis[rt]=1;
22         int d=g[rt].size(); 
23         for (int i=c[rt];i<=m;i++) dp[rt][i]=w[rt]; 
24         for (int i=0;i<d;i++)
25         {
26                 int son=g[rt][i]; 
27                 if (vis[son]==0)
28                 { 
29                         TREEDP(son);
30                 
31                         for (int j=m;j>=c[rt];j--)
32                         {
33                             
34                                 for (int k=1;j+k<=m;k++)
35                                 {
36                                         if (dp[son][k])
37                                         dp[rt][j+k]=max(dp[rt][j+k],dp[rt][j]+dp[son][k]); 
38                         
39                                 } 
40                         } 
41                 } 
42         
43         } 
44 } 
45 void init()
46 {
47         memset(dp,0,sizeof(dp));
48         for (int i=0;i<=n;i++) g[i].clear();
49         memset(vis,0,sizeof(vis)); 
50 } 
51 int main()
52 {
53         //freopen("D:\in.txt","r",stdin); 
54         while (~scanf("%d%d",&n,&m)) 
55          {
56                  if (n==-1 && m==-1) break; 
57                 init(); 
58                 for (int i=1;i<=n;i++)
59                 {
60                         int a,b;
61                         scanf("%d%d",&a,&b);
62                         c[i]=(a+19)/20;
63                     //    if (a%20) c[i]+=1;
64                         w[i]=b; 
65                 } 
66                 for (int i=0;i<n-1;i++)
67                 {
68                         int u,v;
69                         scanf("%d%d",&u,&v);
70                         g[u].push_back(v);
71                         g[v].push_back(u); 
72                 } 
73                 if (m==0)
74                 {
75                         printf("0\n");
76                         continue; 
77                 } 
78                 TREEDP(1);
79                 printf("%d\n",dp[1][m]); 
80         
81         } 
82         return 0; 
83 } 

                     http://acm.hdu.edu.cn/showproblem.php?pid=4003

                     题意差不多,但要特别处理0的情况;          

View Code
 1 #include<cstdio>
 2 #include<cstring>
 3 #include<cstdlib>
 4 #include<algorithm>
 5 #include<iostream>
 6 #include<vector>
 7 #include<cmath>
 8 using namespace std;
 9 const int N=10010;
10 int dp[N][11];
11 int n,s,num;
12 int vis[N];
13 struct node
14 {
15         int v,w;
16 };
17 vector<node > g[N];
18 const int INF=1000000000;
19 
20 
21 void TREEDP(int rt)
22 {
23         vis[rt]=1;
24         int d=g[rt].size();
25         for (int i=0;i<d;i++)
26         {
27                 int v=g[rt][i].v;
28                 int w=g[rt][i].w;
29                 if (vis[v]) continue;
30                 TREEDP(v);
31                 for (int j=num;j>=0;j--)
32                 {
33                         int t=INF;
34                         for (int k=0;k<=j;k++)
35                         {
36                             
37                                 if (j-k==0)
38                                  t=min(t,dp[rt][k]+dp[v][j-k]+w*2);
39                                  else t=min(t,dp[rt][k]+dp[v][j-k]+w*(j-k));
40                                  
41                         }
42                         dp[rt][j]=t;
43                 }
44         }
45         
46 }
47 void init()
48 {
49         memset(dp,0,sizeof(dp));
50         memset(vis,0,sizeof(vis));
51         for (int i=0;i<=n;i++) g[i].clear();
52 
53 }
54 int main()
55 {
56         //freopen("D:\in.txt","r",stdin);
57         while (~scanf("%d%d%d",&n,&s,&num))
58         {
59                 init();
60                 for (int i=0;i<n-1;i++)
61                 {
62                         int u,v,w;
63                         scanf("%d%d%d",&u,&v,&w);
64                         node t1={v,w};
65                         g[u].push_back(t1);
66                         node t2={u,w};
67                         g[v].push_back(t2);
68 
69                 }
70             //    init_dp(s);
71                 memset(vis,0,sizeof(vis));
72                 TREEDP(s);
73                 printf("%d\n",dp[s][num]);
74         }
75         return 0;
76 }

                     http://acm.hdu.edu.cn/showproblem.php?pid=2242

                     这题主要还是缩点;树形DP,就随便搞一下;

                    

View Code
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstdlib>
#include<algorithm>
#include<stack> 
using namespace std;
const int M=20005;
const int N=10005;
const int INF=1000000000; 
struct node
{
    int u,v;
    int next; 
}edge[M*2],edge1[M*2];
int head[N],tol,cnt,dfn[N],low[N],vis[N],belong[N];
int toll,head1[N],val[N],val1[N]; 
int n,m,SUM,Min,scc;
stack<int > S; 
void insert(int a,int b)
{
        node v={a,b,head[a]};
        edge[tol]=v;
        head[a]=tol++; 
} 
void insert1(int a,int b)
{
        node v={a,b,head1[a]};
        edge1[toll]=v;
        head1[a]=toll++; 
} 
void tarjan(int u,int father)
{
        int j,v,flag;
        dfn[u]=low[u]=cnt++; 
        vis[u]=1;
        S.push(u); 
        flag=0;
        for (j=head[u];j!=-1;j=edge[j].next) 
        {
                v=edge[j].v; 
                  if (v==father && !flag)
                {
                       flag=1;continue; 
                } 
                if (!vis[v]) tarjan(v,u);
                low[u]=min(low[u],low[v]); 
        } 
        if (dfn[u]==low[u])
        {
                scc++;
                do
                {
                    v=S.top();S.pop(); 
                    belong[v]=scc;
                    val[scc]+=val1[v]; 
                } while (v!=u) ; 
        } 
} 
int dfs(int u,int father)
{
        int j,v,sum;
        sum=val[u];
        for (j=head1[u];j!=-1;j=edge1[j].next) 
          {
                v=edge1[j].v; 
                  if (v==father) continue;
                sum+=dfs(v,u); 
        } 
        Min=min(Min,abs(SUM-2*sum)) ; 
        return sum; 
} 
int main()
{
     while (~scanf("%d%d",&n,&m))
    {
            memset(head,-1,sizeof(head));
            tol=cnt=0;
            SUM=0;
            for (int i=0;i<n;i++)
            {
                    scanf("%d",&val1[i]);
                    SUM+=val1[i]; 
            } 
            for (int i=0;i<m;i++)
            {
                     int u,v; 
                    scanf("%d%d",&u,&v); 
                    insert(u,v);
                    insert(v,u); 
            } 
            memset(vis,0,sizeof(vis));
            memset(val,0,sizeof(val)); 
              scc=0;
              while (!S.empty())S.pop(); 
            tarjan(0,0); 
            if (scc==1)
            {
                    printf("impossible\n");
                    continue; 
            } 
            toll=0;
            memset(head1,-1,sizeof(head1));
            for (int i=0;i<tol;i++)
            {
                    int a=edge[i].u;
                    int b=edge[i].v; 
                      if (belong[a]!=belong[b])
                    insert1(belong[a],belong [b]); 
            } 
            Min=INF; 
            dfs(1,0);
            printf("%d\n",Min); 
    } 

     return 0; 
} 

             

                    

                    

                                                                     

            

                    

                                                                     

            

原文地址:https://www.cnblogs.com/Rlemon/p/2622493.html