LUOGU 2465 [SDOI2008]山贼集团 状压DP+树形DP

版权声明:https://blog.csdn.net/huashuimu2003 https://blog.csdn.net/huashuimu2003/article/details/89438538

title

LUOGU 2465
题目描述

某山贼集团在绿荫村拥有强大的势力,整个绿荫村由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,表示山贼集团设置所有分部后能够获得的最大收益。

输入输出样例
输入样例#1:

2 1
1 2
2
1
1
3 1 1

输出样例#1:

5

说明

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

analysis

其实不用多叉树转二叉树,只需要跑一遍正常的树上背包就行了。树上背包的模板,详见P1273有线电视网。

我们先预处理出 v a l [ ] val[ ] 数组, v a l [ i ] val[i] 代表一个节点同时被且仅被 i i 的二进制数中是 1 1 的位的分部占领得到的收益,如 i = 11 i=11 ,二进制为 1011 1011 ,表示被 1 , 2 , 4 1,2,4 号分部占领的收益。

f [ i ] [ j ] f[i][j] 为第 i i 号村庄的子树(包括 i i 村),内部有 j j 状态的分部(状态的意义同 v a l val )的最大价值,初始状态为 j j 状态的分部都建立在 i i 村的代价。

f [ i ] [ j ] = i j + i v a l [ j ] i f[i][j]=所有以i的儿子为根的子树,包含分部状态一共为j的最大值+i号村庄对答案的贡献(即val[j],子树中所有村庄都占领了i村) 。——独秀平川

code

#include<bits/stdc++.h>
using namespace std;
const int maxn=110,maxp=4100;
template<typename T>inline void read(T &x)
{
	x=0;
	T f=1, ch=getchar();
	while (!isdigit(ch) && ch^'-') ch=getchar();
	if (ch=='-') f=-1, ch=getchar();
	while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
	x*=f;
}
int ver[maxn<<1],Next[maxn<<1],head[maxn],len;
inline void add(int x,int y)
{
	ver[++len]=y,Next[len]=head[x],head[x]=len;
}
int n,p;
int val[maxp],f[maxn][maxp];
inline void dfs(int x,int fa)
{
	for (int i=head[x]; i; i=Next[i])
	{
		int y=ver[i];
		if (y==fa) continue;
		dfs(y,x);
		for (int j=(1<<p)-1; j; --j)
			for (int k=j; k; k=(k-1)&j)
				f[x][j]=max(f[x][j],f[x][j^k]+f[y][k]);
	}
	for (int i=(1<<p)-1; i; --i)
		f[x][i]+=val[i];
}
int w[maxn][13];
int main()
{
	read(n);read(p);
	for (int i=1; i<n; ++i)
	{
		int x,y;
		read(x);read(y);
		add(x,y);add(y,x);
	}
	for (int i=1; i<=n; ++i)
	{
		for (int j=0; j<p; ++j)
			read(w[i][j]);
		f[i][0]=0;
		for (int j=1; j<(1<<p); ++j)
		{
			int s=j&-j;
			int id=(log(s)+0.001)/log(2);
			f[i][j]=f[i][j^s]-w[i][id];
		}
	}
	int t;read(t);
	for (int i=1; i<=t; ++i)
	{
		int v,c,s=0;
		read(v);read(c);
		for (int j=1; j<=c; ++j)
		{
			int x;read(x);
			s|=1<<x-1;
		}
		val[s]+=v;
		int tot=(1<<p)-1,tmp=s^tot;
		for (int j=tmp; j; j=(j-1)&tmp)
			val[s|j]+=v;
	}
	dfs(1,0);
	printf("%d\n",f[1][(1<<p)-1]);
	return 0;
}

多叉转二叉

// luogu-judger-enable-o2
#include<bits/stdc++.h>
using namespace std;
const int maxn=210,maxp=4100,inf=-1061109568;
template<typename T>inline void read(T &x)
{
    x=0;
    T f=1, ch=getchar();
    while (!isdigit(ch) && ch^'-') ch=getchar();
    if (ch=='-') f=-1, ch=getchar();
    while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
    x*=f;
}
int ver[maxn],Next[maxn],head[maxn>>1],deg[maxn>>1],len;
inline void add(int x,int y)
{
    ver[++len]=y,Next[len]=head[x],head[x]=len,++deg[x];
}
int son[maxn][2],id;
bool vis[maxn];
inline void dfs(int x)
{
    int now=x;
    vis[x]=1;
    for (int i=head[x]; i; i=Next[i])
    {
        int y=ver[i];
        if (vis[y]) continue;
        if (!son[now][0]) son[now][0]=y;
        else if (deg[x]==1) son[now][1]=y;
        else son[now][1]=++id,now=id,son[now][0]=y;
        --deg[x],--deg[y],dfs(y);
    }
}

int n,p;
int VAL[maxp],val[maxp];
int w[maxn][13],cost[maxn][maxp];
int f[maxn][maxp],g[maxn][maxp];
inline int G(int x,int s);

inline int F(int x,int s)
{
    if (f[x][s]>inf) return f[x][s];
    if (x>n) return G(x,s);
    f[x][s]=VAL[s]-cost[x][s];
    for (int i=s; i; i=(i-1)&s)
        f[x][s]=max(f[x][s],G(x,i)-cost[x][i^s]+VAL[s]);
    return f[x][s];
}

inline int G(int x,int s)
{
    if (!son[x][0] && !son[x][1]) return inf;
    if (g[x][s]>inf) return g[x][s];
    if (!son[x][1])
        g[x][s]=F(son[x][0],s);
    else if (!son[x][0])
        g[x][s]=F(son[x][1],s);
    else
    {
        for (int i=s; i; i=(i-1)&s)
            g[x][s]=max(g[x][s],F(son[x][0],i)+F(son[x][1],i^s));
        g[x][s]=max(g[x][s],F(son[x][1],0)+F(son[x][1],s));
    }
    return g[x][s];
}

int main()
{
    read(n);read(p);id=n;
    for (int i=1; i<n; ++i)
    {
        int x,y;
        read(x);read(y);
        add(x,y);add(y,x);
    }
    for (int i=1; i<=n; ++i)
        for (int j=1; j<=p; ++j)
            read(w[i][j]);
    int t;read(t);
    for (int i=1; i<=t; ++i)
    {
        int v,c,s=0;read(v);read(c);
        for (int j=1; j<=c; ++j)
        {
            int x;read(x);
            s|=1<<x-1;
        }
        val[s]+=v;
    }
    dfs(1);
    for (int i=0; i<(1<<p); ++i)
    {
        for (int j=i; j; j=(j-1)&i)
            VAL[i]+=val[j];
        for (int j=1; j<=n; ++j)
            for (int k=1; k<=p; ++k)
                if (i&(1<<k-1))
                    cost[j][i]+=w[j][k];
    }
    memset(f,192,sizeof(f));
    memset(g,192,sizeof(g));
    printf("%d\n",F(1,(1<<p)-1));
    return 0;
}

猜你喜欢

转载自blog.csdn.net/huashuimu2003/article/details/89438538