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

  

posted on 2012-08-08 17:21  夜->  阅读(237)  评论(0编辑  收藏  举报