http://acm.hdu.edu.cn/showproblem.php?pid=4340
题意:
题意:两个人进攻n个城市的国家,这n个城市构成一棵树,可以任意选择一个点开始,攻击已被自己攻击过的点的相邻点,时间可以减半,两个人的攻击时间不一
这个状态要求这个连通块有一个入口嘛
显然题中给图的是一颗树。问题可以抽象成对树的每个点都染色,有两中颜色可以选择。
我们可以知道,如果某一个连通的点集染的是同一种颜色,则这个集合中只要而且必须有一个点取完整的费用,其他的点都只需要对应费用的一半。
状态:dp[i][j][k] (0 <= i <= n, 0<=j<=1, 0 <= k <= 1) 表示以i为根的子树的费用,其中i节点被染成了第j种颜色,且子树中与i染成同一种颜色的与i连通的点集有k个点选取了完整的费用。
若选取1为根节点,则最后需要的结果为: min(dp[1][0][1], dp[1][1][1]}。
状态转移方程:
v为i节点的儿子节点。令 S = sum{min(dp[v][j][0], dp[v][1-j][1])}, det = min{dp[v][j][1] - min(dp[v][j][0], dp[v][1-j][1])};
dp[i][j][0] = cost[i][j]/2 + S;
dp[i][j][1] = min(cost[i][j] + S, cost[i][j]/2 + S + det);
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<set>
#include<map>
#include<queue>
#include<vector>
#include<string>
#define Min(a,b) a<b?a:b
#define Max(a,b) a>b?a:b
#define CL(a,num) memset(a,num,sizeof(a));
#define maxn 205
#define inf 9999999
#define mx 1<<60
using namespace std;
vector<__int64>g[maxn];
int dp[maxn][2][2],a[2][maxn],vis[maxn];
void dfs(int k)
{
int i,j;
int len = g[k].size();
if( len == 0)// 为叶子节点
{
dp[k][0][0] = a[0][k]/2;
dp[k][0][1] = a[0][k];
dp[k][1][0] = a[1][k]/2;
dp[k][1][1] = a[1][k];
return ;
}
for( i = 0;i < 2; ++i)
{
int sum = 0,det = inf ,tmp;
for( j = 0; j < len ;++j)
{
dfs(g[k][j]);
int w = g[k][j];
tmp = min(dp[w][i][0],dp[w][1 - i][1]);
sum +=tmp;
det = min(det,dp[w][i][1] - tmp);
}
dp[k][i][0] = sum + a[i][k]/2;
dp[k][i][1] = min(a[i][k] + sum ,a[i][k]/2 + sum + det);
}
}
int main()
{
int n, i, j,x,y,root;
while(scanf("%d",&n)!=EOF)
{
for(j = 0; j < 2 ;++j)
{
for( i = 1; i <= n ;++i )
scanf("%d",&a[j][i]);
}
CL(vis,0);
for(i = 0;i <= n;i++)g[i].clear();
for(i = 0; i < n - 1 ;++i)
{
scanf("%d%d",&x,&y);
if( i ==0) root = x;//建的图是有向图,对解决问题没有影响,树形dp 就是 这样
if(!vis[y])
{
g[x].push_back(y);
vis[x] = 1;
}
else g[y].push_back(x);
}
CL(dp,0);
dfs(root);
int t1 = dp[root][0][1];
int t2 = dp[root][1][1];
printf("%d\n",min(t1,t2));
}
}