2018 ICPC亚洲区域赛网络赛 南京 E AC Challenge (状压DP)赛后补题

弄了差不多一下午 东查查西找找终于把题目算是弄懂了...

也不算懂 算是个半懂吧....多刷几道状压DP题就好了。

通过集合枚举每个状态,找到哪个状态是由哪个状态转化而来的(枚举每个状态集合的子集)

如果这个子集集合和某个题目的要求的集合相等那么 

那么就让这个状态dp【i】和dp【u】(前置状态dp数组)+做对了题目数量*a【j+1】+b【j+1】相比较

更新最大值

最后枚举每个状态取最大就行了。

 /*
 qq:1239198605
 ctgu_yyf
        */

#include<iostream>
#include<cstdio>
#include<string>
#include<vector>
#include<queue>
#include<stack>
#include<cstring>
#include<algorithm>
#include<cmath>
#define ll long long
using namespace std;
const int maxn=1<<21;
const int inf=0x3f3f3f3f;
ll dp[maxn];

ll qian[maxn],a[maxn],b[maxn];

ll count(ll x)
{
	ll num=0;
	for(int j=0;j<25;j++)
	if(x&(1<<j)) num++;
	
	return num;
}
 
int main()
{
  int n,m;
  scanf("%d",&n);
  
  for(int i=1;i<=n;i++) 
  {
  	scanf("%lld%lld%d",&a[i],&b[i],&m);
  	qian[i]=0;
  	for(int j=0;j<m;j++)
  	{
  		int k;
  		scanf("%d",&k);
  		
  		//转换成二进制存储 做出这道题前 要先做哪些题 
  		qian[i]+=(1<<(k-1));	
	}
  }
  
  ll ans=0;
  
  for(int i=0;i<=(1<<n)-1;i++)
  dp[i]=-inf;
  
  dp[0]=0;
  
  for(int i=1;i<=(1<<n)-1;i++)
   for(int j=0;j<n;j++)
   {
   	if(i&(1<<j))//枚举子集 
   	{
   		int u=i-(1<<j);
   		if((u&qian[j+1])==qian[j+1])
   		{
   			ll w=dp[u]+count(i)*a[j+1]+b[j+1];
   			dp[i]=max(dp[i],w);
   			ans=max(ans,dp[i]);			
		   }
	   }
   }
   
   printf("%lld\n",ans);


return 0;
}

猜你喜欢

转载自blog.csdn.net/k_koris/article/details/82350900