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;
}

 

posted @ 2019-02-25 22:44  DukeLv  阅读(231)  评论(0编辑  收藏  举报