题意:有n个人要和m个敌人打比赛,要打g场比赛,每天最多打一场,持续g+10天,然后给出n个人中每个人打敌人的胜率,且一个人打了比赛后需要休息4天,然后给出第i天要打的敌人序号,0表示不打,问最多能得多少分(胜率累加和)。
题解:dp的思路比较好想,f[i][x][j][k][l]表示在第i天,第i-1天是第x个人打的,第i-2天是第j个人打的,第i-3天是第k个人打的,第i-4天是第l个人打的情况下最大得分,先把打每个敌人胜率从大到小排序,然后每次只取前5个人(休息四天,要和之前的人比对是否是同一人)。
状态转移方程:
如果第i天要打比赛:
f[i][x][j][k][l] = max(f[i][x][j][k][l],f[i - 1][j][k][l][o] + p[g[i]][x])
不打:
f[i][0][j][k][l] = max(f[0][x][j][k][l],f[i - 1][j][k][l][o])
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 105;
struct Point {
int v, id;
}p[N][N];
int n, m, d, g[N * 3], res;
int f[2][10][10][10][10];
bool cmp(Point a, Point b) {
return a.v > b.v;
}
int judge(int x, int y) {
if (x == 0 || y == 0)
return 0;
return p[x][y].id;
}
int main() {
int t;
scanf("%d", &t);
while (t--) {
scanf("%d%d%d", &n, &m, &d);
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
scanf("%d", &p[i][j].v);
p[i][j].id = j;
}
sort(p[i] + 1, p[i] + 1 + n, cmp);
}
res = 0;
d += 10;
for (int i = 1; i <= d; i++)
scanf("%d", &g[i]);
memset(f[0], 0, sizeof(f[0]));
for (int i = 1; i <= d; i++) {
int now = i % 2;
int pre = 1 - now;
memset(f[now], 0, sizeof(f[now]));
if (g[i]) {
for (int x = 1; x <= 5; x++) {
for (int j = 0; j <= 5; j++) {
if (i > 1 && judge(g[i], x) == judge(g[i - 1], j))
continue;
for (int k = 0; k <= 5; k++) {
if (i > 2 && judge(g[i], x) == judge(g[i - 2], k))
continue;
for (int l = 0; l <= 5; l++) {
if (i > 3 && judge(g[i], x) == judge(g[i - 3], l))
continue;
for (int o = 0; o <= 5; o++) {
if (i > 4 && judge(g[i], x) == judge(g[i - 4], o))
continue;
f[now][x][j][k][l] = max(f[now][x][j][k][l], f[pre][j][k][l][o] + p[g[i]][x].v);
res = max(res, f[now][x][j][k][l]);
}
}
}
}
}
}
else {
for (int j = 0; j <= 5; j++)
for (int k = 0; k <= 5; k++)
for (int l = 0; l <= 5; l++)
for (int o = 0; o <= 5; o++) {
f[now][0][j][k][l] = max(f[now][0][j][k][l], f[pre][j][k][l][o]);
res = max(res, f[now][0][j][k][l]);
}
}
}
printf("%.2lf\n", res * 1.0 / 100);
}
return 0;
}