山贼集团 (group)

博客围绕山贼集团在绿荫村分部选址问题展开。绿荫村由N个连通小村落组成,总部在1号村落,P个部门要建分部。需考虑建设费用、分部相互影响代价,编写程序确定分部位置使集团获最大收益,还给出了树上背包等解题思路。

山贼集团 (group)

 

题目描述

 

某山贼集团在绿荫村拥有强大的势力,整个绿荫村由N个连通的小村落组成,并且保证对于每两个小村落有且仅有一条简单路径相连。小村落用阿拉伯数字编号为1,2,3,4,…,n,山贼集团的总部设在编号为1的小村落中。山贼集团除了老大坐镇总部以外,其他的P个部门希望在村落的其他地方建立分部。P个分部可以在同一个小村落中建设,也可以分别建设在不同的小村落中。每个分部到总部的路径称为这个部门的管辖范围,于是这P个分部的管辖范围可能重叠,或者完全相同。在不同的村落建设不同的分部需要花费不同的费用。每个部门可能对他的管辖范围内的小村落收取保护费,但是不同的分部如果对同一小村落同时收取保护费,他们之间可能发生矛盾,从而损失一部分的利益,他们也可能相互合作,从而获取更多的利益。现在请你编写一个程序,确定P个分部的位置,使得山贼集团能够获得最大的收益。

输入

 

输入文件第一行包含一个整数N和P,表示绿荫村小村落的数量以及山贼集团的部门数量。

接下来N-1行每行包含两个整数X和Y,表示编号为X的村落与编号为Y的村落之间有一条道路相连。(1<=X,Y<=N)

接下来N行,每行P个正整数,第i行第j个数表示在第i个村落建设第j个部门的分部的花费Aij。

然后有一个正整数T,表示下面有T行关于山贼集团的分部门相互影响的代价。(0<=T<=2^p)

 

最后有T行,每行最开始有一个数V,如果V为正,表示会获得额外的收益,如果V为负,则表示会损失一定的收益。然后有一个正整数C,表示本描述涉及的分部的数量,接下来有C个数,Xi,为分部门的编号(Xi不能相同)。表示如果C个分部Xi同时管辖某个小村落(可能同时存在其他分部也管辖这个小村落),可能获得的额外收益或者损失的收益为的|V|。T行中可能存在一些相同的Xi集合,表示同时存在几种收益或者损失。

 

输出

 

    输出文件要求第一行包含一个数Ans,表示山贼集团设置所有分部后能够获得的最大收益。

 

样例输入

2 1
1 2
2
1
1
3 1 1

样例输出

5

提示

 

数据规模

    对于40%的数据,1<=P<=6。

 

    对于100%的数据,1<=N<=100,1<=P<=12,保证答案的绝对值不超过108。


solution

 

令f[k][S]表示在第k棵子树,安排人员状态为S的最大收益(不一定都在k,也可以在k的子树上安排)。

然后发现暴力转移复杂度很高(穷举儿子的安排状态)。

于是我们考虑在树上背包,得出儿子的最优解。

令g[i][S]表示k的前i棵子树,安排人员状态S的最大收益。

g[i][S]=g[i-1][S']+f[v][S-S']    (v是第i个儿子)

g[top][S]即为所有子树的答案  (共有top个儿子)

f[k][S]=g[top][S']+cost(k,S-S')+val(S)

cost为在k安排的花费,val为收益

枚举2^n,在枚举每个数真子集

效率O(可以过)

似乎是3^n,但我不会证。。。

枚举子集的方法:

for(int j=i;j>=0;j=((j-1)&i)){//枚举i的子集
    .....
    if(!j)break;
}
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#define maxn 102
#define inf 1e9
using namespace std;
int n,p,head[maxn],f[maxn][1<<13],g[maxn][1<<13];
int t1,t2,tot,cost[maxn][12],t,tt,top,Max;
struct node{
    int v,nex;
}e[maxn*2];
struct no{
    int zt,v,num;
}c[1<<13];
void lj(int t1,int t2){
    e[++tot].v=t2;e[tot].nex=head[t1];head[t1]=tot;
}
void mer(int a,int id){
    if(id==1){
        for(int i=0;i<=Max;i++)g[id][i]=f[a][i];
        return;
    }
    for(int i=0;i<=Max;i++){
        g[id][i]=-inf;
        for(int j=i;j>=0;j=((j-1)&i)){
            int S=i-j;
            g[id][i]=max(g[id][i],g[id-1][j]+f[a][S]);
            if(!j)break;
        }
    }
}
int co(int fs,int k){
    int sum=0;
    for(int i=0;i<p;i++){
        if((k&(1<<i))>0)sum-=cost[fs][i+1];
    }
    return sum;
}
int val(int k){
    int sum=0;
    for(int i=1;i<=t;i++){
        int S=k|c[i].zt;
        if(S==k)sum+=c[i].v;
    }
    return sum;
}
void dfs(int k,int fa){
    for(int i=head[k];i;i=e[i].nex){
        if(e[i].v!=fa){
            dfs(e[i].v,k);
        }
    }
    top=1;int fl=0;
    for(int i=head[k];i;i=e[i].nex){
        if(e[i].v!=fa){
            fl=1;mer(e[i].v,top);top++;
        }
    }
    top--;
    //cout<<"k "<<k<<' '<<top<<endl;
    if(!fl){    
        for(int i=0;i<=Max;i++){
            f[k][i]=co(k,i)+val(i);
            //cout<<f[k][i]<<' ';
        }
        //cout<<endl;
        return;
    }
    //cout<<"fuck\n";
    for(int i=0;i<=Max;i++){
        f[k][i]=-inf;
        for(int j=i;j>=0;j=((j-1)&i)){
            int S=i-j;
            //cout<<"aaa "<<j<<' '<<S<<' '<<i<<' '<<co(k,j)<<' '<<g[top][S]<<endl;
            f[k][i]=max(f[k][i],co(k,j)+g[top][S]);
            if(!j)break;
        }
        //cout<<"fsy "<<i<<' '<<f[k][i]<<endl;
        f[k][i]+=val(i);
         
        //cout<<"haha "<<f[k][i]<<' '<<i<<' '<<val(i)<<endl ;
    }
    //cout<<endl;
}
int main()
{
 
    cin>>n>>p;
    Max=(1<<p)-1;
    for(int i=1;i<n;i++){
        scanf("%d%d",&t1,&t2);
        lj(t1,t2);lj(t2,t1);
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=p;j++){
            scanf("%d",&t);
            cost[i][j]=t;
        }
    }
    cin>>t;
    for(int i=1;i<=t;i++){
        scanf("%d%d",&c[i].v,&c[i].num);
        for(int j=1;j<=c[i].num;j++){
            scanf("%d",&tt);
            c[i].zt+=(1<<(tt-1));
        }
    }
    dfs(1,0);
    cout<<f[1][Max]<<endl;
    return 0;
}
 

 

转载于:https://www.cnblogs.com/liankewei/p/10358850.html

生成c++代码,从代码逻辑角度完善更改 肉鸽生存游戏——武侠客栈 主角:客栈小伙计 游戏主流程: >>主事件(day i=1) * 触发主事件 事件:1. [遇敌](流寇、马匪、山贼、恶僧、无理官兵、侠客切磋、路见不平) 2.[奇遇](观摩高手对决、捡拾物品、顿悟、结交、偷学武学) 3.[收入](发工资-银子、获得武学/生活经验、发现宝藏) * 结算奖励 奖励:1.[战利品](战斗获得奖励) 2.[属性收益](奇遇获得的非物品奖励) 3.[经验收益](人物/武学经验获得) 4.[物品获得](银子、物品、装备、武学、道具) >>进入副事件(5行动点/day) * [领悟](学武学2点) * [练武](增加武学/基本武功经验1点) * [购物](购买商店物品1点) * [交互](与npc互动--交谈、切磋、送礼、请教1点) * [强化](强化装备1点) * [突破](突破武学瓶颈1点) >>结束回合(进入下一天i+1) >>游戏结束 * 人物hp<=0--lose * i>=30--victory 分模块: * 角色状态及属性模块 * 游戏窗口及场景切换模块 * 互动与战斗模块 * 辅助标识模块(物品新旧区分,物品/武侠品质,颜色) * 地图模块 * 经验等级熟练度成长模块 * 刷新模块(商店,任务,状态) * 关卡难度变化模块 * 初始随机模块 * 瓶颈模块(关卡boss,武侠突破,物品强化)
最新发布
05-12
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值