自学内容网 自学内容网

【状压dp】 Cunning Gena

Cunning Gena

在这里插入图片描述


分析:

很明显的一个状压dp
f [ i ] [ j ] f[i][j] f[i][j]表示前i个人,组成解决问题的状态为j的最少花费
由于状态不好从前面转移(状态不唯一)
所以我们从前面往后面转移
第i个人的解题状态我们也用一个状压数组去表示(假设为s[i])
那么我们就有如下转移:
f [ i ] [ j ∣ s [ i ] ] = m i n ( f [ i ] [ j ∣ s [ i ] ] , f [ i − 1 ] [ j ] + x [ i ] ) f[i][j|s[i]]=min(f[i][j|s[i]],f[i-1][j]+x[i]) f[i][js[i]]=min(f[i][js[i]],f[i1][j]+x[i])
由于当前只和前面一维有关,所以我们可以采用滚动数组。
采用滚动数组的时候需要注意,我们在当前这轮状态的时候很可能还保留着前一次状态的信息,所以我们每次重启一轮,都要重新赋值,放置前一轮的信息对这一轮产生影响。

但是这道题还有一个需要注意的点就是显示器的问题。
我们将人按照显示器从小到大排序,然后显示器的数量就有了单调性。
对于第i个人,当前的答案就是f数组的答案加上显示器的答案。


#include<bits/stdc++.h>
using namespace std;

#define ll long long
//#define inf 2e18+10
//const ll inf=2e18+10;
const ll N = (1<<20)+1,inf = 2e18+10;
ll f[2][N];
struct Node{
ll x,k;
int m,s;
}a[800];
int n,m;
ll b;

bool cmp(Node x,Node y){
return x.k < y.k;
}

inline int re(){
  int f = 1,x = 0;
  char ch = getchar();
  while(!isdigit(ch)){
if(ch == '-')f = -1;
  ch = getchar();
  }
  while(isdigit(ch)){
  x = (x << 1) + (x << 3) + (ch ^ 48);
  ch = getchar();
  }
  return x * f;
}

ll Min(ll x,ll y){
return x>y?y:x;
}

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cin>>n>>m>>b;
for (int i = 1; i <= n; i++){
cin>>a[i].x>>a[i].k>>a[i].m;
for (int j = 1,x; j <= a[i].m; j++)
  cin>>x,a[i].s|=(1<<(x-1));
}
for (int j = 1; j < (1<<m); j++) f[0][j] = inf;
sort(a+1,a+n+1,cmp);
ll ans = inf;
for (int i = 1; i <= n; i++){
for (int j = 0; j < (1<<m); j++) f[i&1][j] = inf;
for (int j = 0; j < (1<<m); j++){
    if (f[i+1&1][j] == inf) continue;
f[i&1][j|a[i].s] = Min(f[i&1][j|a[i].s],f[i+1&1][j]+a[i].x);
f[i&1][j] = min(f[i+1&1][j],f[i&1][j]);
}
ans = Min(ans,f[i&1][(1<<m)-1]+a[i].k*b);
}
printf("%lld\n",ans==inf?-1:ans);
return 0;
}

原文地址:https://blog.csdn.net/huang_ke_hai/article/details/140594250

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!