http://acm.hdu.edu.cn/showproblem.php?pid=4340
树型dp 理解起来并不难但是状态有点多 比赛的时候没敢写
解题上好像是用的三维数组 有两个维大小是2 的
自己干脆写了6个一维数组 然后6个dp函数相互调用 虽然代码有点长
但是理解方便 思路也比较清晰
对予一个子树的根节点 有6中方法
1 A从这里进攻
2 B从这里进攻
3 A攻击这里时间花一半 因为上面的相邻城市A 已经提前攻破
4 B-------------------------------------
5 A攻击这里花一半时间 因为下面的某个相邻城市已经被A提前攻破
6 B-----------------------------------
对于没种状态 向下选取攻击方法的时候 选择合适的 最恰当的
代码及其注释:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<map>
#include<cmath>
#define LL long long
using namespace std;
const int N=105;
const int M=0x3f3f3f3f;
struct node
{
struct tt *next;
}mem[N];
struct tt
{
int j;
struct tt *next;
};
int ansa[N];//A 直接攻击这里 花费全部时间
int ansb[N];//B 直接攻击这里 花费全部时间
int ansa0[N];// A 攻击 一半时间 上面提前相邻的已被 A 攻破
int ansb0[N];// B 攻击 一半时间 上面提前相邻的已被 B 攻破
int ansa1[N];// A 攻击 一半时间 下面提前相邻的已被 A 攻破
int ansb1[N];// B 攻击 一半时间 下面提前相邻的已被 B 攻破
int a[N],b[N];
void build(int i,int j)
{
struct tt *t=new tt;
t->j=j;
t->next=mem[i].next;
mem[i].next=t;
}
void Dele(int n)
{
for(int i=1;i<=n;++i)
mem[i].next=NULL;
}
int MIN(int x,int y,int z)
{
if(x>y)
x=y;
if(x>z)
x=z;
return x;
}
int dpb(int ,int);
int dpa0(int ,int);
int dpb0(int ,int);
int dpa1(int ,int);
int dpb1(int ,int);
int dpa(int x,int pre)
{
if(ansa[x]!=-1)
return ansa[x];
ansa[x]=a[x];// A花费全部时间攻击这里
struct tt *t=mem[x].next;
while(t!=NULL)
{
if(t->j!=pre)
{
ansa[x]+=MIN(dpa0(t->j,x),dpb(t->j,x),dpb1(t->j,x));//再攻击下面的点 A 在攻击就可以花费一半时间 B的话就不行了
}
t=t->next;
}
return ansa[x];
}
int dpb(int x,int pre)
{
if(ansb[x]!=-1)
return ansb[x];
struct tt *t=mem[x].next;
ansb[x]=b[x];
while(t!=NULL)
{
if(t->j!=pre)
{
ansb[x]+=MIN(dpb0(t->j,x),dpa(t->j,x),dpa1(t->j,x));
}
t=t->next;
}
return ansb[x];
}
int dpa0(int x,int pre)
{
if(ansa0[x]!=-1)
return ansa0[x];
ansa0[x]=a[x]/2;// A 花费一半时间 攻击这里 上面相邻城市 A已经提前攻破
struct tt *t=mem[x].next;
while(t!=NULL)
{
if(t->j!=pre)
{
ansa0[x]+=MIN(dpa0(t->j,x),dpb(t->j,x),dpb1(t->j,x));//下面 A 攻击花一半时间 B的话有两种
}
t=t->next;
}
return ansa0[x];
}
int dpb0(int x,int pre)
{
if(ansb0[x]!=-1)
return ansb0[x];
ansb0[x]=b[x]/2;
struct tt *t=mem[x].next;
while(t!=NULL)
{
if(t->j!=pre)
{
ansb0[x]+=MIN(dpb0(t->j,x),dpa(t->j,x),dpa1(t->j,x));
}
t=t->next;
}
return ansb0[x];
}
int dpa1(int x,int pre)
{
if(ansa1[x]!=-1)
return ansa1[x];
int temp=a[x]/2;//A花费一半时间 下面某个相邻的城市已被提前攻破 先保存下面城市不用提前攻破的情况
struct tt *t=mem[x].next;
while(t!=NULL)
{
if(t->j!=pre)
{
temp+=MIN(dpa0(t->j,x),dpb(t->j,x),dpb1(t->j,x));
}
t=t->next;
}
ansa1[x]=M;
t=mem[x].next;
while(t!=NULL)
{
if(t->j!=pre)
{
int k=MIN(dpa0(t->j,x),dpb(t->j,x),dpb1(t->j,x));
ansa1[x]=MIN(ansa1[x],temp-k+dpa(t->j,x),temp-k+dpa1(t->j,x));//枚举下面哪个城市 是提前攻破的(提前攻破 有直接全部时间攻破 和 再把这样状态向下传递两个情况)
}
t=t->next;
}
return ansa1[x];
}
int dpb1(int x,int pre)
{
if(ansb1[x]!=-1)
return ansb1[x];
int temp=b[x]/2;
struct tt *t=mem[x].next;
while(t!=NULL)
{
if(t->j!=pre)
{
temp+=MIN(dpb0(t->j,x),dpa(t->j,x),dpa1(t->j,x));
}
t=t->next;
}
ansb1[x]=M;
t=mem[x].next;
while(t!=NULL)
{
if(t->j!=pre)
{
int k=MIN(dpb0(t->j,x),dpa(t->j,x),dpa1(t->j,x));
ansb1[x]=MIN(ansb1[x],temp-k+dpb(t->j,x),temp-k+dpb1(t->j,x));
}
t=t->next;
}
return ansb1[x];
}
int main()
{
//freopen("data.txt","r",stdin);
int n;
while(scanf("%d",&n)!=EOF)
{
for(int i=1;i<=n;++i)
scanf("%d",&a[i]);
for(int i=1;i<=n;++i)
scanf("%d",&b[i]);
for(int i=1;i<n;++i)
{
int x,y;
scanf("%d %d",&x,&y);
build(x,y);
build(y,x);
}
memset(ansa,-1,sizeof(ansa));
memset(ansb,-1,sizeof(ansb));
memset(ansa0,-1,sizeof(ansa0));
memset(ansb0,-1,sizeof(ansb0));
memset(ansa1,-1,sizeof(ansa1));
memset(ansb1,-1,sizeof(ansb1));
int ans=M;
ans=MIN(ans,dpa(1,0),dpb(1,0));//从1 这个节点进行dp 只有这4 种状态 选最小的那个
ans=MIN(ans,dpa1(1,0),dpb1(1,0));
printf("%d\n",ans);
Dele(n);
}
return 0;
}