题意:N个人对M道题目都有自己通过的期望,每道题目要派一个人出战,任意两人出战次数不得超过1,求成绩的最大期望 (1 ≤ N ≤ 10,1 ≤ M ≤ 1000)。
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5045
——>>设dp[i][j]表示已出战了前i - 1道题目,已出战的人的状态序列为j,现在要做第i道题目的最大期望,则最后要求的结果为max{dp[M][k]}。。
状态转移方程:dp[i][nState] = max(dp[i][nState], dp[i - 1][j] + fSolve[k][i]);
将M进行N个N个地分块,为了让当前块加上上一个分块的结果,则要将上一个分块完成时的结果存入当前块要利用的初始状态的结果。即:
if (nState == (1 << N) - 1) { nState = 0; }
上代码:
#include <cstdio> #include <cstring> #include <algorithm> using std::max; const int MAXN = 10; const int MAXM = 1000 + 5; int N, M; double fSolve[MAXN + 1][MAXM]; double dp[MAXM][1 << MAXN]; double fRet; void Read() { scanf("%d%d", &N, &M); for (int i = 1; i <= N; ++i) { for (int j = 1; j <= M; ++j) { scanf("%lf", &fSolve[i][j]); } } } int CalOnes(int nNum) { int nRet = 0; while (nNum) { if (nNum & 1) { nRet++; } nNum >>= 1; } return nRet; } void Dp() { fRet = 0.0; memset(dp, 0, sizeof(dp)); for (int i = 1; i <= M; ++i) { for (int j = 0; j < (1 << N); ++j) { for (int k = 1; k <= N; ++k) { if (j & (1 << (k - 1))) continue; if (CalOnes(j) != (i - 1) % N) continue; int nState = j | (1 << (k - 1)); if (nState == (1 << N) - 1) { nState = 0; } dp[i][nState] = max(dp[i][nState], dp[i - 1][j] + fSolve[k][i]); if (i == M) { fRet = max(fRet, dp[i][nState]); } } } } } int main() { int T, nCase = 0; scanf("%d", &T); while (T--) { Read(); Dp(); printf("Case #%d: %.5f\n", ++nCase, fRet); } return 0; }