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));
}
}