AC Challenge (状压DP)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/PK__PK/article/details/82620471

题目链接:https://nanti.jisuanke.com/t/30994

题意:有n道题目,每道题目有两个值,a,b。若 你在 t 分钟时做了某道题,那么你会获得 a * t + b的分数。但是做某道题时,有限制条件,你想要先做完了另外一些题目才能做这道题。比如 在做第三题时,第三题要求必须先做完第一题和 第二题,那么你要想做第三题 必须先做完第一题和第二题,问如何做题才能最后分数最大。

题解:一看就是一道状压dp,但是场上没想出来,没想到状态中1的个数就是当前时间这一点,导致没写出来。

dp【i】:表示当前状态的最大得分,i为0|1的二进制数,0表示没做,1表示做了。

c【i】:表示第i道题的限制条件,用0|1表示

那么就能理解为:若某个状态 state 中 第 i 题做了 ,那么 他一定是有 state^(1<<i)这个状态转移过来的。遍历所有状态 然后 取出最大值即可。 

#include<bits/stdc++.h>
using namespace std;
#define ll long long 
const int mo = 1e9+7;
const int maxn = 1 << 20;
const ll inf = 0x3f3f3f3f;
int n,m,k,T;
ll dp[maxn],ans,tmp,sum;
ll a[25],c[25],b[25];

ll fcount (int x){
	ll s = 0 ;
	while(x){
		s ++;
		x &= (x-1);
	}
	return s;
}

int main(){
	while(scanf("%d",&n) != EOF){
		ans = 0 ;
		for(int i = 0 ; i < n ; i ++){
			scanf("%lld%lld%lld",&a[i],&b[i],&k);
			c[i] = 0;
			for(int j = 0 ; j < k ; j ++){
				int x;
				scanf("%d",&x);
				x --; // 我的题目 编号从 0 开始 ,故需要-1 
				c[i] |= (1<< x);
			}
		}
		m = 1 << n;
		for(int i = 0 ; i < m ; i ++)
			dp[i] = -inf;
		dp[0] = 0;  // 最开始状态 的得分一定 为 0 
		for(int i = 1 ; i < m ; i ++){
			for(int j = 0 ; j < n ; j ++){
				if(i & (1<< j)){  // 若 i 题 做了 
					int tmp  = i ^ (1<<j);  // tmp 为 i 没做的状态 
					if(dp[tmp] == -inf) continue;  // 若没有状态 能到达 tmp 就证明 不合法 跳过 
					if((tmp|c[j]) == tmp){  // 判断 tmp这个 状态 是否 能满足 做 i 题的 条件 
						ll t = fcount(i);  // 计算 i 状态 的 时间  
						dp[i] = max(dp[i],dp[tmp] + a[j] * t + b[j]); // 转移 
						ans = max(ans,dp[i]); // 维护最大值。 
					}
				} 
			}
		}
		printf("%lld\n",ans);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/PK__PK/article/details/82620471
今日推荐