UVa 10817

题目链接:https://onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=1758

(dp[i][s1][s2]) 表示前 (i) 个人,被恰好一个人教的课程集合为 (s1),被至少两个人教点的课程集合为 (s2),因为状态的原因,转移只能使用刷表法

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

const int maxn = 130;
const int INF = 0x3f3f3f3f;

int s, m, n;
int c[maxn], sub[maxn];
int dp[maxn][1 << 9][1 << 9];

ll read(){ ll s = 0, f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){ if(ch == '-') f = -1; ch = getchar(); } while(ch >= '0' && ch <= '9'){ s = s * 10 + ch - '0'; ch = getchar(); } return s * f; }

int main(){
	while(scanf("%d%d%d", &s, &m, &n) == 3 && s){
		memset(sub, 0, sizeof(sub));
		string in;
		for(int i = 1 ; i <= m + n ; ++i){
			scanf("%d", &c[i]);
			getline(cin, in);
			
			int len = in.length();
			for(int j = 0 ; j < len ; ++j){
				if(in[j] >= '0' && in[j] <= '9'){
					sub[i] |= (1 << (in[j] - '1'));
				}
			}
		}
		
		memset(dp, 0x3f, sizeof(dp));
		
		dp[0][0][0] = 0;
		for(int i = 0 ; i < m + n ; ++i){
			for(int s1 = 0 ; s1 < (1 << s) ; ++s1){
				for(int s2 = 0 ; s2 < (1 << s) ; ++s2){
					if(s1 & s2) continue;
					
					int s0 = ((1 << s) - 1) ^ (s1 | s2);
					// 不选 
					if(i+1 > m) dp[i+1][s1][s2] = min(dp[i+1][s1][s2], dp[i][s1][s2]);
					// 选	
					int ns2 = s2 | (s1 & sub[i+1]);
					int ns1 = (s1^(s1 & sub[i+1])) | (s0 & sub[i+1]);
					dp[i+1][ns1][ns2] = min(dp[i+1][ns1][ns2], dp[i][s1][s2] + c[i+1]); 
				}
			}
		}
		
		printf("%d
", dp[m+n][0][(1<<s)-1]);
	}
	return 0;
}
原文地址:https://www.cnblogs.com/tuchen/p/15072629.html