题意:你作为星河站队的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 110
int 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 105
struct 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 110
int 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;
}
}
其实对于这个代码还有可以优化的地方,只是这样写比较简单,并且也可以过,所以就省了,如果谁有优化方法,欢迎留言交流!