CF#321-D - Kefa and Dishes-状压DP+bitmasks

http://codeforces.com/contest/580/problem/D

题意

给出n个菜,要选出m个

每个菜有一个权值aa[i]

另有k个额外的条例

条例格式 X Y C:  如果先吃第X个菜,马上接着吃Y个菜,会得到额外的权值C 

求n个菜选m个的条件下,可以得到的最大权值

思路:

由于n=18,所以我们可以用一个数的二进制位,来表示有没选第i道菜,也就是用一个数来表示当前 选择的菜的状态

i从0表示一个不选  到 i=2^n-1 表示所有菜都选了

dp[i][j]  表示  在状态i情况下,以第j个菜为最后一个菜的情况下  得到的最大权值;

由于只有18个菜,我们可以遍历2^18次方种情况,每一个数换算成二进制,如果第i位 为0表示不选,1表示选该第i个菜

伪代码:

for (i=0; i< 2^18;i++)
{
	把i转为二进制,计算1个个数 (表示选了多少道菜)
	如果个数>m,continue; 因为只需要选m个,超过了就不用理了
	如果个数==m,则更新一下答案。
	个数小于m的时候,说明还需要继续选菜:
		枚举n种情况,以每一种菜为最后一个菜
		for(j=0;j<17;j++)
		{
			if(dp[i][j]==-1) 表示 当前状态 根本没选j,所以不存在以j为最后一菜的情况 ;continue;
			i状态下,j位最后一菜的情况下,菜数不够m,现在我们要再选一道菜,也是遍历每一道菜
			for (k=0;k<n;k++)
			{
				if (i&(1<<k))  //如果为真,i状态的二进制的第k个位为1,表示第k个菜已经选过了
					continue;
				 //下面是 第k个菜没选过的情况

				// i|(1<<k) 表示把第k个菜选上的状态(把i的二进制的第k位置为一)
				dp[i|(1<<k)][k]=max(dp[i|(1<<k)][k],dp[i][j]+map[j][k]+aa[k]); //更新状态
			 	//dp[i][j]+map[j][k]+aa[k]  表示i状态下,以j结尾的情况,再选了第k菜作为结尾得到的权值
			}   

		}
			
}


ac代码:


#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <iostream>
#include <queue> 
#include <set>
#include <vector>
using namespace std;


__int64 map[19][19];
__int64 max(__int64 a,__int64 b)
{return a>b?a:b;}
__int64 n,m,k;
__int64 aa[20];
__int64 dp[262144+5][18+2];
int main()
{ 
	__int64 n,i,j,k ; 
	scanf("%I64d%I64d%I64d",&n,&m,&k);
	__int64 a,b,c; 
	for (i=0;i<n;i++)
	{
		scanf("%I64d",&aa[i]);
	}
	for (i=0;i<k;i++)
	{
		scanf("%I64d%I64d%I64d",&a,&b,&c);
		map[a-1][b-1]=c; 
	}
	
	memset(dp,-1,sizeof(dp));
	__int64 cun=(__int64)pow(2.0,n);
	__int64 tmp;
	
	__int64 ans=0;
	
	for (i=0;i<n;i++)
	{
		dp[1<<i][i]=aa[i];
	} 
	
	for (i=0;i<cun;i++)
	{
		
		tmp=i;
		__int64 cou=0;
		while(tmp)
		{
			if (tmp&1)
				cou++;
			tmp=tmp>>1; 
		}
		__int64 flag=0;
		if (cou==m) 
			flag=1;

		if (cou>m) continue;
		
		for (j=0;j<n;j++)
		{	
			if (dp[i][j]==-1)//如果该状态不包含第j个dish
				continue;
			
			if (flag)  //如果当前状态已经够m个dish,
				ans=max(ans,dp[i][j]);
			
			for (k=0;k<n;k++)
			{
				if (i&(1<<k))  //如果第k个菜已经选了
					continue;
				
				// i|(1<<k) 表示把第k个dish选上的状态
				dp[i|(1<<k)][k]=max(dp[i|(1<<k)][k],dp[i][j]+map[j][k]+aa[k]);
			}
		}
	}
	printf("%I64d\n",ans);	 
	
	
	return 0;
	
}


你可能感兴趣的:(CF#321-D - Kefa and Dishes-状压DP+bitmasks)