1. 程式人生 > >ACM-ICPC 2018 南京賽區網路預賽 : E. AC Challenge(狀壓DP)

ACM-ICPC 2018 南京賽區網路預賽 : E. AC Challenge(狀壓DP)

這道題出的很棒,是狀壓DP的入門題目,簡單說一下題意

每道題會有前置的做題需求,給我們題目的價值表示方法,要我們求出做這些題目可以獲得的做大價值。

由於最多隻有20題,直接狀壓一下然後暴力就可以了。dp[i]表示狀態為i時的最大價值

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef long long ll;
typedef unsigned int ui;
#define INF 0x3f3f3f3f
#define PB push_back
#define PI 3.1415926
#define SF scanf
#define PF printf
#define mem(a,x) memset(a,x,sizeof(a))
int gcd(int p, int q) {return q==0?p:gcd(q,p%q);}

const int maxn = 25;

struct node{
	int a, b, c;
}s[maxn];

LL dp[1<<22];
int cnt[1<<22];

void init(){
	for(int i = 0; i < 1<<(21); i++){
		int n = i, s;
		for(s = 0; n; ++s){
			n &= (n-1);
		}
		cnt[i] = s;
	} 
}

int main(){
	int n;
	init();
//	for(int i = 0; i <= 5; i++){
//		cout << cnt[i] << endl;
//	}
	while(scanf("%d", &n) != EOF){
		mem(s, 0);
		mem(dp, -INF);
		dp[0] = 0;
		for(int i = 1; i <= n; i++){
			int ss, p;
			scanf("%d%d%d", &s[i].a, &s[i].b, &ss);
			int sp = 0;
			while(ss--){
				scanf("%d", &p);
				sp = sp | (1 << (p - 1));
			}
			s[i].c = sp;
		}
		LL ans = 0;
		for(int i = 1; i < (1<<(n+1)); ++i){
			for(int j = 1; j <= n; ++j){
				if(i & (1<<(j-1))){
					LL tmp = i^(1<<(j-1));
					if((tmp&s[j].c) == s[j].c){
						dp[i] = max(dp[i], dp[tmp] + cnt[i]*s[j].a + s[j].b);
					}
				}
				ans = max(ans, dp[i]);
			}
		}
		printf("%lld\n", ans);
	}
	return 0;
} 

/*
5
5 6 0
4 5 1 1
3 4 1 2
2 3 1 3
1 2 1 4
1
-100 0 0

55
0
*/