Greetings!(枚举子集+dp)

版权声明:欢迎大佬指正! https://blog.csdn.net/sinat_36215255/article/details/82942533

题目网址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;
}

猜你喜欢

转载自blog.csdn.net/sinat_36215255/article/details/82942533