HDU 4340 Capturing a country
想了好久才看懂大牛的结题报告,附上链接:http://blog.csdn.net/cyberzhg/article/details/7840922
自己理解,整理下:刚开始的时候陷入了一个误区,枚举所有点把他们当树根,然后递推:dp[rt][0]+=min(dp[son][0]-ca,dp[son][1]);很明显这个递推是每次全部取固定为子树根了,情况不完全;也就是二维不能完全表示状态;
dp[N][2][2]
dp[rt][0][0]:表示以rt为树根的子树根节点选择了A,但是与根节点A联通的块内(即各个子树的根也选了A,由根节点也选A,连通起来的连通块)没有一个是完全花费的(没有确定起点);
dp[rt][0][0]=sum( min( dp[son][0][0],dp[son][1][1] ) )+A[rt]/2;这个很容易理解,每一棵子树只能由两个选择;
如果选了dp[son][0][1],那么dp[rt][0][0]是不符合逻辑的跟定义违背,dp[son][1][0]也是错误的;
dp[rt][0][1]=sum(同上)+min( A[rt], A[rt]/2+EA );
EA=min( dp[son][0][1]-min(dp[son][0][0],dp[son][1][1]) );蓝色min是针对rt的各个子树的; dp[rt][0][1]因为要在联通块内找一个A入口,那么入口肯定在根节点或者在子树内,如果在根节点那么只要sum()+A[rt]就可以了,因为入口在跟结点所以在子树中就不需要入口,多入口只会增加费用。
如果在子树内,那么根节点就不需要,但其中一棵子树就要有一个A入口,所以其中一棵子树要选dp[soni][0][1],所以只要找出最小的就可以了;
EA :假设子树i被选为有入口的子树,那么dp[rt][0][1]=sum()-min( dp[soni][0][0],dp[soni][1][1] )+dp[soni][0][1] +A[rt]/2;
代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 #include<vector> 7 using namespace std; 8 const int N=110; 9 vector<int > g[N]; 10 int A[N],B[N]; 11 int n; 12 int dp[N][2][2];//dp[i][0][0]表示以i为根结点的子树i选A, 13 //且其中关于A的连通中全部去半值; 14 int dmin(int a,int b){ 15 if (a==-1) return b; 16 if (a<b) return a;return b; 17 } 18 void TREEDP(int rt,int fa){ 19 20 int SA=0,SB=0,EA=-1,EB=-1; 21 int flag=0; 22 for (int i=0;i<g[rt].size();i++){ 23 int c=g[rt][i]; 24 if (c==fa) continue; 25 flag=1; 26 TREEDP(c,rt); 27 SA+=min(dp[c][0][0],dp[c][1][1]); 28 SB+=min(dp[c][1][0],dp[c][0][1]); 29 EA=dmin(EA,dp[c][0][1]-min(dp[c][0][0],dp[c][1][1])); 30 EB=dmin(EB,dp[c][1][1]-min(dp[c][1][0],dp[c][0][1])); 31 } 32 if (flag==0){ 33 dp[rt][0][0]=A[rt]/2;dp[rt][0][1]=A[rt]; 34 dp[rt][1][0]=B[rt]/2;dp[rt][1][1]=B[rt]; 35 return; 36 } 37 dp[rt][0][0]=SA+A[rt]/2; 38 dp[rt][1][0]=SB+B[rt]/2; 39 dp[rt][0][1]=min(SA+A[rt],SA+A[rt]/2+EA); 40 dp[rt][1][1]=min(SB+B[rt],SB+B[rt]/2+EB); 41 42 } 43 int main(){ 44 while (~scanf("%d",&n)){ 45 for (int i=0;i<n;i++) scanf("%d",&A[i+1]); 46 for (int j=0;j<n;j++) scanf("%d",&B[j+1]); 47 for (int i=0;i<=n;i++) g[i].clear(); 48 for (int i=0;i<n-1;i++){ 49 int u,v;scanf("%d%d",&u,&v); 50 g[u].push_back(v); 51 g[v].push_back(u); 52 } 53 int ans; 54 TREEDP(n-1,0);//任选一个作为根; 55 ans=min(dp[n-1][0][1],dp[n-1][1][1]); 56 57 printf("%d\n",ans); 58 59 } 60 return 0; 61 }