【状压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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值