HDU 5074 Hatsune Miku(DP)
题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5074
题目大意:给定一个长度为n的序列c[i],该序列为一个乐谱。我们的任务是要给这个序列补上音符。c[i]的数值有两种情况:-1、不是-1。不是-1的这个位置的音符已经确定,不能再更改。是-1的这个位置音符可以任意选择。可以选择的音符有m种,编号为1-m。
当乐谱序列完成后,会有一个得分产生(即这乐谱的优美程度)。得分为score(c[1],c[2])+score(c[2],c[3])+...+score(c[n-1],c[n])。其中score[i][j]在题中已经给出。我们的任务是要把这个得分总和最大化。
思路:显然是DP。由于我之前回顾了区间DP,因而……先想到了区间DP。但是这样显然不行。从DP的无后效性考虑,我大概想到了方法。。
设f[i][j]为序列c[1]到c[i],并且c[i]=j时可以得到的最大得分。则可分4种情况讨论:
1、c[i - 1] !=-1, c[i] != -1.这是最简单的情况,定死的。直接算即可。
2、c[i - 1] == -1, c[i] != -1.此时只要对f[i - 1][j]进行枚举更新即可。
3、c[i - 1] != -1,c[i] == -1.这种情况对f[i][j]进行枚举,从f[i - 1][c[i - 1]]更新即可。
4、c[i - 1] == -1,c[i] != -1.该情况的状态转移方程为f[i][j] = max(f[i - 1][k] + score[k][j]).
时间复杂度为O(NM^2)
代码送上(代码中的a[i][j]即为这里的score[i][j])
1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #include <algorithm> 5 #include <functional> 6 7 using namespace std; 8 9 #define REP(i,n) for(int i(0); i < (n); ++i) 10 #define rep(i,a,b) for(int i(a); i <= (b); ++i) 11 #define INF 1 << 30 12 13 14 const int Q = 1000 + 10; 15 int f[Q][Q], a[Q][Q]; 16 int c[Q]; 17 int n, m; 18 int T; 19 20 int main(){ 21 22 scanf("%d", &T); 23 REP(Case, T){ 24 memset(f, 0, sizeof f); memset(a, 0, sizeof a); memset(c, 0, sizeof c); 25 scanf("%d%d", &n, &m); 26 rep(i, 1, m) rep(j, 1, m) scanf("%d", &a[i][j]); 27 rep(i, 1, n) scanf("%d", c + i); 28 rep(i, 1, n) f[1][i] = 0; 29 rep(i, 2, n){ 30 if (c[i] != -1 && c[i - 1] != -1) f[i][c[i]] = f[i - 1][c[i - 1]] + a[c[i - 1]][c[i]]; 31 if (c[i] != -1 && c[i - 1] == -1) rep(j, 1, m) f[i][c[i]] = max(f[i][c[i]], f[i - 1][j] + a[j][c[i]]); 32 if (c[i] == -1 && c[i - 1] != -1) rep(j, 1, m) f[i][j] = f[i - 1][c[i - 1]] + a[c[i - 1]][j]; 33 if (c[i] == -1 && c[i - 1] == -1) rep(j, 1, m) rep(k, 1, m) f[i][j] = max(f[i][j], f[i - 1][k] + a[k][j]); 34 } 35 int ans = 0; 36 if (c[n] == -1) rep(i, 1, m) ans = max(ans, f[n][i]); else ans = f[n][c[n]]; 37 printf("%d\n", ans); 38 } 39 40 41 return 0; 42 43 }