P2465 [SDOI2008]山贼集团 dp
这个题是一道树形dp+状压dp二合一,先预处理每种组合会有什么额外的费用,然后在树上dp就行了。
题干:
题目描述 某山贼集团在绿荫村拥有强大的势力,整个绿荫村由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<=2p) 最后有T行,每行最开始有一个数V,如果V为正,表示会获得额外的收益,如果V为负,则表示会损失一定的收益。然后有一个正整数C,表示本描述涉及的分部的数量,接下来有C个数,Xi,为分部门的编号(Xi不能相同)。表示如果C个分部Xi同时管辖某个小村落(可能同时存在其他分部也管辖这个小村落),可能获得的额外收益或者损失的收益为的|V|。T行中可能存在一些相同的Xi集合,表示同时存在几种收益或者损失。 输出格式: 输出文件要求第一行包含一个数Ans,表示山贼集团设置所有分部后能够获得的最大收益。
代码:
#include<iostream> #include<cstdio> #include<cmath> #include<ctime> #include<queue> #include<algorithm> #include<cstring> using namespace std; #define duke(i,a,n) for(register int i = a;i <= n;++i) #define lv(i,a,n) for(register int i = a;i >= n;--i) #define clean(a) memset(a,0,sizeof(a)) const int INF = 1 << 30; typedef long long ll; typedef double db; template <class T> void read(T &x) { char c; bool op = 0; while(c = getchar(), c < '0' || c > '9') if(c == '-') op = 1; x = c - '0'; while(c = getchar(), c >= '0' && c <= '9') x = x * 10 + c - '0'; if(op) x = -x; } template <class T> void write(T x) { if(x < 0) putchar('-'), x = -x; if(x >= 10) write(x / 10); putchar('0' + x % 10); } const int N = 110; int lst[N],len = 0; int dp[N][4200]; int val[4200]; struct node { int l,r,nxt; }a[N << 1]; int n,m; void add(int x,int y) { a[++len].l = x; a[len].r = y; a[len].nxt = lst[x]; lst[x] = len; } void initdp() { int cost[N][13]; duke(i,1,n) { duke(j,0,m - 1) { read(cost[i][j]); } dp[i][0] = 0; for(int j = 1;j < (1 << m);++j) { int lowbit = j & (-j); int lowid = (log(lowbit) + 0.001) / log(2); dp[i][j] = dp[i][j ^ lowbit] - cost[i][lowid]; } } } void calval(int x) { duke(i,1,x) { int v,cnt,s = 0; read(v);read(cnt); duke(j,1,cnt) { int mem; read(mem); s |= (1 << (mem - 1)); } int maxm = (1 << m) - 1; val[s] += v; int tmp = s ^ maxm; for(int j = tmp;j;j = (j - 1) & tmp) { val[(s | j)] += v; } } } void dfs(int now,int fa) { for(int k = lst[now];k;k = a[k].nxt) { int y = a[k].r; if(y != fa) { dfs(y,now); for(int j = (1 << m) - 1;j;j--) { for(int i = j;i;i = (i - 1) & j) { dp[now][j] = max(dp[now][j],dp[now][j ^ i] + dp[y][i]); } } } } for(int i = (1 << m) - 1;i;i--) { dp[now][i] += val[i]; } } int main() { read(n);read(m); duke(i,1,n - 1) { int x,y; read(x);read(y); add(x,y); add(y,x); } initdp(); int t; read(t); // cout<<t<<endl; calval(t); dfs(1,0); printf("%d\n",dp[1][(1 << m) - 1]); return 0; }
只想找一个不会伤害我的人