题目网址https://nanti.jisuanke.com/t/32227
题意:
给n个信(信封有长宽和数目),问在找最多k种信封类型的时候,最少浪费多少纸
思路:
看了看题解,深以为然,这个思路很巧妙,也许是我太菜,没细想。
我们首先二进制枚举计算出,每种状态下,这些信合用同一种信封时的浪费数目。
然后通过枚举子集,dp[ i ] [ j ] 代表 i 种信封,现在已经装了 j 集合的信封的最小浪费数。
很显然,对于每种ij 我们从j的子集种去获取最小值,那么就是枚举 j 的子集,然后从之前已经退出的 dp[i-1] [ prej ] 中获得最小答案。
具体实现看代码,感觉思路很清晰很巧妙。
状压果然优秀。
代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <iostream>
#define ll long long
using namespace std;
const int maxn = (1<<15)+20;
ll dp[20][maxn];
int W[maxn],H[maxn];
ll Q[maxn],C[maxn],cost[maxn];
int w[maxn],h[maxn],q[maxn];
int main()
{
int n,k;
scanf("%d%d",&n,&k);
for(int i=0; i<n; i++)
{
scanf("%d%d%d",&w[i],&h[i],&q[i]);
}
for(int i=0; i<(1<<n); i++)
{
for(int j=0; j<n; j++)
{
if(i&(1<<j))
{
W[i] = max(W[i],w[j]);
H[i] = max(H[i],h[j]);
Q[i] +=q[j];
C[i] +=((long long)w[j]*h[j]*q[j]);
}
}
cost[i] = (long long)W[i]*H[i]*Q[i] - C[i];
// cout<<cost[i]<<" "<<W[i]<<" "<<C[i]<<endl;
}
const ll inf = 1e12*20;
for(int i=0; i<=k;i++)
for(int j=0; j<(1<<n); j++)
dp[i][j] = inf;
dp[0][0] = 0;
for(int i=1; i<=k; i++)
for(int j=0; j<(1<<n); j++)
{
ll cnt = inf;
for(int k=j; k>0; k = ((k-1)&j))
{
cnt = min(cnt,dp[i-1][j-k]+cost[k]);
}
// if(j==(1<<n)-1)
// cout<<cnt<<endl;
dp[i][j] = cnt;
}
ll ans = inf;
int d = (1<<n)-1;
for(int i=1; i<=k; i++)
ans = min(ans,dp[i][d]);
printf("%lld\n",ans);
return 0;
}