题目链接:http://codeforces.com/gym/101002
将容纳卡片的集合压缩后用j表示,dp【i】【j】用i个信封容纳集合j中的卡片。之后枚举j的子集,从i-1个信封中转移而来,最后输出dp【k】【(1<<n)-1】
另外今天学到了一种最快速遍历子集的方法%%%https://www.cnblogs.com/jffifa/archive/2012/01/16/2323999.html
#include<bits/stdc++.h>
#define inf 1e16
#define mod 1000000007
#define For(i,m,n) for(int i=m;i<=n;i++)
#define Dor(i,m,n) for(int i=m;i>=n;i--)
#define LL long long
#define lan(a,b) memset(a,b,sizeof(a))
#define sqr(a) a*a
using namespace std;
LL dp[20][32800];
LL a[20],b[20],c[20];
int main()
{
int n,k;
while(~scanf("%d%d",&n,&k))
{
for(int i=1;i<=n;i++)
scanf("%lld%lld%lld",&a[i],&b[i],&c[i]);
for(int i=1;i<=k;i++)
for(int j=0;j<1<<n;j++)
dp[i][j]=inf;
dp[0][0]=0;
for(int i=0;i<=(1<<n)-1;i++)
{
LL maxa=0,maxb=0;
LL num=0,sum=0;
int tem=i;
int id=1;
while(tem)
{
if(tem&1)
maxa=max(maxa,a[id]),maxb=max(maxb,b[id]),num+=c[id],sum+=c[id]*a[id]*b[id];
tem>>=1;
id++;
}
dp[1][i]=num*maxa*maxb-sum;
}
for(int i=2;i<=k;i++)
{
for(int j=0;j<=(1<<n)-1;j++)
{
for(int x=j;x;x=(x-1)&j)
for(int k=1;k<i;k++)
dp[i][j]=min(dp[i][j],dp[i-k][x]+dp[k][j-x]);//printf("dp %d %d=%lld\n",i,j,dp[i][j]);
}
}
printf("%lld\n",dp[k][(1<<n)-1]);
}
return 0;
}