hdu 1011 树型DP(依赖背包)

来源:互联网 发布:四种软件体系结构 编辑:程序博客网 时间:2024/05/23 01:13

 题意:你作为星河站队的leader,手下有m个trooper;现在让你去攻占一个基地:有n个洞穴组成,入口是洞穴1,洞穴之间用n-1条边链接,每个洞穴里面包括x个bugs,和他们的brains,你的每个trooper可以消灭20个bugs;问你最多可以得到多少个brains。

需要注意的是:你没做过的叶子必须要留人,也就是说就算某个地方的bug是0个,你也要牌一个trooper过去。

写这个题目的时候,开始用的是以前的依赖背包的思想,而且代码也是按照依赖背包写的,写的比较长,也比较繁琐,主要思路是:

       因为入口是1,所以从1开始,设P=1,P是访问到的节点编号,设dp[i]表示i是需要的trooper数量,当作价格,dp[i]是得到的brains数量,当作价值:

       1、遍历P的所有孩子,如果P的孩子i还有孩子,P=i,转到1,遍历完以后,如果所有的孩子都没有子孩子,转到2,否组转到3;

       2、对所有的孩子进行01背包:初始化dp都是P的价值,然后进行01背包,然后把dp[i]对应的值都后移P的价格。因为如果想到到达P的孩子,必须到达P。然后回溯到上一层1,继续遍历P;

       3、由于P的有些孩子还有孩子,那么处理的时候就要按照不同的情况进行01和分组背包处理,对于没有孩子的孩子,按照01背包处理,对于有孩子的孩子,对其进行分组背包的处理,然后回溯到上一层1继续遍历P;

代码如下:

/*#include <iostream>#include <cstdio>#include <cstring>#include <cmath>#include <vector>using namespace std;#define N 110int num,total_price,dp[N][N];struct Room{    int price,value;}room[N];bool visited[N];vector <int> connected[N];void init(){    int i,x,y;    memset(dp,0,sizeof(dp));    memset(visited,true,sizeof(visited));    for(i=0;i<=num;i++) connected[i].clear();    for(i=1;i<=num;i++)    {        scanf("%d%d",&room[i].price,&room[i].value);        room[i].price=ceil(room[i].price/20.0);        if(room[i].price>total_price)        visited[i]=false;    }    for(i=1;i<num;i++)    {        scanf("%d%d",&x,&y);        connected[x].push_back(y);        connected[y].push_back(x);    }}void dfs(int key){    visited[key]=false;    int i,j,k;    for(i=room[key].price;i<=total_price;i++) dp[key][i]=room[key].value;    for(i=0;i<(int)connected[key].size();i++)    {        int s=connected[key][i];        if(!visited[s]) continue;        dfs(s);        for(j=total_price;j>=room[key].price;j--)        {            for(k=1;k+j<=total_price;k++)            {                if(dp[s][k])                dp[key][j+k]=max(dp[key][j+k],dp[key][j]+dp[s][k]);            }        }    }}int main(){    while(cin>>num>>total_price&&((num+1)||(total_price+1)))    {        init();        if(!total_price)        {            cout<<"0"<<endl; continue;        }        dfs(1);        cout<<dp[1][total_price]<<endl;    }}*/#include <cstdio>#include <cstring>#include <iostream>#include <cmath>#include <list>using namespace std;#define N 105struct Room{    int price,value;}room[N*N];bool map[N][N],de_room[N],de_map[N];list <int>tree[N];int num,total_price,ans,num_i,num_j;void update(int key){    de_room[key]=false;    int i;    tree[key].clear();    for(i=1;i<=num;i++)    {        if(map[key][i]&&de_room[i])        {            tree[key].push_back(i);            de_room[i]=false;        }    }    list <int>::iterator p=tree[key].begin();    while(p!=tree[key].end())    {        update(*(p++));    }}void fun(int key){    int dp[N],i,k;    memset(dp,0,sizeof(dp));    list <int> ::iterator p=tree[key].begin();    while(p!=tree[key].end())    {        if(tree[*p].empty())        {            k=0;            if(room[*p].price==0)            k=1;            for(i=total_price;i>=room[*p].price+k;i--)            {                dp[i]=max(dp[i],dp[i-room[*p].price-k]+room[*p].value);            }        }        else        {            fun(*p);            for(i=total_price;i>=0;i--)            {                list <int> ::iterator q=tree[*p].begin();                while(q!=tree[*p].end())                {                    if(i>=room[*q].price)                    dp[i]=max(dp[i],dp[i-room[*q].price]+room[*q].value);                    q++;                }            }            tree[*p].clear();        }        p++;    }    tree[key].clear();    if(key==num_i)    {        if(total_price-room[num_i].price>=0)        ans=max(ans,dp[total_price-room[num_i].price]+room[num_i].value);        return ;    }    for(i=total_price;i>=0;i--)    {        if(dp[i]<=0)        {            room[++num_j].price=room[key].price;            if(room[key].price==0)            room[num_j].price+=1;            room[num_j].value=room[key].value;            tree[key].push_back(num_j);            break;        }        if(i+room[key].price<=total_price)        {            room[++num_j].price=i+room[key].price;            room[num_j].value=dp[i]+room[key].value;            tree[key].push_back(num_j);        }    }}int main(){    while(scanf("%d%d",&num,&total_price))    {        ans=0;        if(num==-1&&total_price==-1)        return 0;        memset(de_map,true,sizeof(de_map));        memset(map,false,sizeof(map));        int i,x,y;        for(i=1;i<=num;i++)        {            scanf("%d%d",&room[i].price,&room[i].value);            room[i].price=ceil(room[i].price/20.0);            if(room[i].price>total_price)            de_map[i]=false;        }        for(i=1;i<num;i++)        {            scanf("%d%d",&x,&y);            if(de_map[x]&&de_map[y])            map[x][y]=map[y][x]=true;        }        if(total_price==0)        {            cout<<"0"<<endl;            continue;        }        memset(de_room,true,sizeof(de_room));        num_i=1;num_j=num;        update(1);        fun(1);        cout<<ans<<endl;    }}

上边的思想和代码都比较繁琐,后来在网上参考了一些代码,用的其实还是依赖背包的思想,只是方法变了。

代码如下:

#include <iostream>#include <cstdio>#include <cstring>#include <cmath>#include <vector>using namespace std;#define N 110int num,total_price,dp[N][N];struct Room{    int price,value;}room[N];bool visited[N];vector <int> connected[N];void init(){    int i,x,y;    memset(dp,0,sizeof(dp));    memset(visited,true,sizeof(visited));    for(i=0;i<=num;i++) connected[i].clear();    for(i=1;i<=num;i++)    {        scanf("%d%d",&room[i].price,&room[i].value);        room[i].price=ceil(room[i].price/20.0);        if(room[i].price>total_price)        visited[i]=false;//小小的优化,就是如果这个点的price比total_price还大,那么就不用访问了    }    for(i=1;i<num;i++)    {        scanf("%d%d",&x,&y);        connected[x].push_back(y);        connected[y].push_back(x);    }}void dfs(int key){    visited[key]=false;    int i,j,k;    for(i=room[key].price;i<=total_price;i++) dp[key][i]=room[key].value;    for(i=0;i<(int)connected[key].size();i++)    {        int s=connected[key][i];        if(!visited[s]) continue;        dfs(s);        for(j=total_price;j>=room[key].price;j--)        {            for(k=1;k+j<=total_price;k++)            {                if(dp[s][k])                dp[key][j+k]=max(dp[key][j+k],dp[key][j]+dp[s][k]);            }        }    }}int main(){    while(cin>>num>>total_price&&((num+1)||(total_price+1)))    {        init();        if(!total_price)        {            cout<<"0"<<endl; continue;        }        dfs(1);        cout<<dp[1][total_price]<<endl;    }}

其实对于这个代码还有可以优化的地方,只是这样写比较简单,并且也可以过,所以就省了,如果谁有优化方法,欢迎留言交流!

原创粉丝点击