gfoj 卡夫的菜

题目:http://www.gdfzoj.com/oj/contest/270/problems/3

有n盘菜,每盘菜都有一个美味值。但是你只能选m盘菜。并且这些吃菜的顺序能影响总美味值。如果i当且仅当在j之前吃,那么会额外加一些美味值,这样的组合有k组。求选m盘菜吃能获得的最大的美味值。 

Input

第一行为n,m,k
第二行n个整数ai(0<=ai<=10^9)代表每盘菜的美味值
接下来k行每行三个整数xi,yi,ci(1<=xi,yi<=n,xi!=yi,0<=ci<=10^9),代表在吃第yi盘菜前吃第xi盘菜会增加第yi盘菜ci的美味值,保证不会有相同的二元组(xi,yi)。

对于50%的数据,1<=n,m<=10
对于100%的数据,1<=n,m<=15

Output

一个整数输出最大的美味值

Sample Input

样例输入1
2 2 1
1 1
2 1 1

样例输入2
4 3 2
1 2 3 4
2 1 5
3 4 2

Sample Output

样例输出1
3

样例输出2
12

 

 

看到这数据范围可以考虑状压,则f的其中一维便是状态

对于这些前后关系,肯定要一维来记录当前状态的最后一盘菜。

so,f[s,i]:最后一盘菜为i的状态s的最大美味值。

公式其实不难推,重点是生成状态

我们要按1的个数从小到大生成,原本是用dfs,这次又限制条件,改良一下即可

void dfs(int state,int pos,int tot)
{
	if (pos>n)
	{
		if (tot==0)
			s[++tmp]=state;
		return ;
	}
	dfs(state,pos+1,tot);
	if (tot>0)
		dfs(state+(1<<(pos-1)),pos+1,tot-1);
}


int main()
{
	for (i=1;i<=m;i++)
		dfs(0,1,i);
}

代码:

#include <cstdio>
#include <algorithm>
#include <cstring>

using namespace std;

const int maxS=15;
long long f[(1<<15)+5][maxS+5];
int a[maxS+5],c[maxS+5][maxS+5],s[800000+5];
int n,m,k1,tmp=0;

void dfs(int state,int pos,int tot)
{
	if (pos>n)
	{
		if (tot==0)
			s[++tmp]=state;
		return ;
	}
	dfs(state,pos+1,tot);
	if (tot>0)
		dfs(state+(1<<(pos-1)),pos+1,tot-1);
}

int main()
{
	int i,x,y,z,j,k;
	
	freopen("a.txt","r",stdin);
	scanf("%d%d%d",&n,&m,&k1);
	
	memset(c,0,sizeof(c));
	memset(f,0,sizeof(f));
	memset(s,0,sizeof(s));
	memset(a,0,sizeof(a));
	for (i=1;i<=n;i++)
		scanf("%d",&a[i]);
	for (i=1;i<=k1;i++)
	{
		scanf("%d%d%d",&x,&y,&z);
		c[x][y]=z;
	}
	for (i=1;i<=m;i++)
		dfs(0,1,i);
		
	for (i=1;i<=n;i++)
		f[(1<<(i-1))][i]=a[i];
	for (i=1;i<=tmp;i++)
	{
		for (j=1;j<=n;j++)
		{
			if (((1<<(j-1))&s[i])==0)
				continue;
			for (k=1;k<=n;k++)
			{
				if (((1<<(k-1))&s[i])>0)
					continue;
				f[s[i]+(1<<(k-1))][k]=max(f[s[i]+(1<<(k-1))][k],f[s[i]][j]+a[k]+c[j][k]);
			}
		}
	}
	long long ans=0;
	for (i=1;i<=tmp;i++)
		for (j=1;j<=n;j++)
			ans=max(ans,f[s[i]][j]);
	printf("%lld\n",ans);
	
	return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值