HDU - 1011Starship Troopers (树形dp+背包)

题目:带领m个士兵,存在n个房间(n-1条通路,1号房间为起点);每个房间有两个参数:bugs数以及价值。
每个士兵可以抵抗20个bugs,参与抵抗的士兵不继续往下走,只有消灭房间中的所有bugs,才可以得到该房间的价值。
要到达下一个房间,必须先将当前房间的bugs全部消除;并且走过的房间就不再到达。
问有m个士兵的情况下,怎样使最后得到的价值最大,最大为多少?

f(i, j) = max { max{ f(i, j-k) + f(v, k) | 1<=k<=j-cost(i) } |  v是i的儿子节点 }

#include <bits/stdc++.h>

using namespace std;
const int maxn=110;
int n,m,val[maxn],w[maxn];
vector<int>a[maxn];
int dp[maxn][maxn];
void dfs(int u,int fa)
{
    if(!w[u]&&a[u].size()==1&&u!=1)//是叶子节点但是没有bug,依然需要士兵过去!
    w[u]++;
    for(int i=w[u];i<=m;i++)
        dp[u][i]=val[u];
    for(int i=0;i<a[u].size();i++)
    {
        int v=a[u][i];
        if(v==fa) continue;
        dfs(v,u);
        for(int j=m;j>=w[u];j--)///背包过程
        for(int k=1;k<=j-w[u];k++)
        {
            dp[u][j]=max(dp[u][j],dp[u][j-k]+dp[v][k]);///方程
        }
    }
}
int main()
{
    ///freopen("in.txt","r",stdin);
    while(scanf("%d%d",&n,&m),n!=-1&&m!=-1)
    {
        for(int i=1;i<=n;i++)
        {
            int tmp;
            scanf("%d%d",&tmp,&val[i]);
            w[i]=tmp/20;
            if(tmp%20) w[i]++;///多出来的bugs
        }
        for(int i=1;i<=n;i++)
            a[i].clear();
        for(int i=1;i<n;i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            a[x].push_back(y);
            a[y].push_back(x);
        }
        memset(dp,0,sizeof dp);
        if(m==0)//判断没有士兵哪个节点也去不了
        {
            puts("0");
            continue;
        }
        dfs(1,-1);

        printf("%d\n",dp[1][m]);
    }
    return 0;
}

写的另一个版本的dp方程思路略有不同,代码比较繁琐

#include <bits/stdc++.h>

using namespace std;
const int maxn=110;
int n,m,val[maxn],w[maxn];
vector<int>a[maxn];
int tmp[maxn],dp[maxn][maxn];
void dfs(int u,int fa)
{
   if(!w[u]&&a[u].size()==1&&u!=1)///
    w[u]++;
    for(int i=0;i<a[u].size();i++)
    {
        int v=a[u][i];
        if(v==fa) continue;
        dfs(v,u);
        memset(tmp,0,sizeof tmp);//暂时存已经入选的子节点的最有情况
        for(int j=0;j<=m;j++)
        for(int k=0;k<=(m-j);k++)
        {
            int t1=dp[u][j],t2=dp[v][k];
            if(!j) t1=0;////判断不为0是因为子节点那也必须得有士兵过去
            if(!k) t2=0;////判断不为0是因为子节点那也必须得有士兵过去
            tmp[j+k]=max(tmp[j+k],t1+t2);
        }
        for(int j=0;j<=m;j++)
        dp[u][j]=tmp[j];///暂时存已经入选的子节点的最有情况
    }
    for(int j=m;j>=w[u];j--)
        dp[u][j]=dp[u][j-w[u]]+val[u];///再把自身需要的w[u]处理一下
    for(int j=0;j<w[u];j++)
        dp[u][j]=0;

}
int main()
{
   // freopen("in.txt","r",stdin);
    while(scanf("%d%d",&n,&m),n!=-1&&m!=-1)
    {
        for(int i=1;i<=n;i++)
        {
            int tmp;
            scanf("%d%d",&tmp,&val[i]);
            w[i]=tmp/20;
            if(tmp%20) w[i]++;///
        }
        for(int i=1;i<=n;i++)
            a[i].clear();
        for(int i=1;i<n;i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            a[x].push_back(y);
            a[y].push_back(x);
        }
        memset(dp,0,sizeof dp);
        if(m==0)///
        {
            puts("0");
            continue;
        }
        dfs(1,-1);
        printf("%d\n",dp[1][m]);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/dllpxfire/article/details/81070592