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