洛谷P1854 花店橱窗布置
此题出自 IOI 1999
此题的难点在于:
1.如何满足题目所述的顺序问题;
2.如何记录转移状态。
首先考虑朴素算法。着眼于问题1,不难想到枚举第一朵花的位置,然后在这个位置左边枚举第二朵花的位置......依此类推。
思考这个模型,发现其搜索树的状态存在大量重叠,且此问题满足“最优子结构”,“子问题重叠性”,“无后效性”三个dp的基础条件。
于是考虑dp。
根据朴素算法,考虑使用一个数组 f[ i ][ j ] 记录前 i 朵花占前 j 个瓶子的最大值。
于是,可以构造出核心转移:
for(int i = 2; i <= F; i++) for(int j = i; j <= V - F + i; j++) for(int k = 1; k < j; k++) if(f[i - 1][k] + B[i][j] > f[i][j]) { f[i][j] = f[i - 1][k] + B[i][j]; pre[i][j] = k; }
容易发现,当 f 的值得到更新,意味着从 k 处得到转移,不难想到问题二的解法:记录状态 f[ i ][ j ]的转移来源即可。
完整代码:
1 // luogu-judger-enable-o2 2 #include <cstdio> 3 #include <algorithm> 4 #include <cstring> 5 #include <iostream> 6 #include <vector> 7 using namespace std; 8 const int MAXF = 1e2 + 20; 9 10 inline int read() 11 { 12 static int x; 13 scanf("%d", &x); 14 return x; 15 } 16 17 int f[MAXF][MAXF], pre[MAXF][MAXF]; 18 int B[MAXF][MAXF]; 19 int F, V; 20 21 int main() 22 { 23 cin>>F>>V; 24 for(int i = 1; i <= F; i++) 25 for(int j = 1; j <= V; j++) 26 B[i][j] = read(); 27 28 memset(f, -0x7f, sizeof(f)); 29 for(int i = 1; i <= (V - F + 1); i++) 30 f[1][i]=B[1][i], pre[1][i] = i; 31 32 for(int i = 2; i <= F; i++) 33 for(int j = i; j <= V - F + i; j++) 34 for(int k = 1; k < j; k++) 35 if(f[i - 1][k] + B[i][j] > f[i][j]) 36 { 37 f[i][j] = f[i - 1][k] + B[i][j]; 38 pre[i][j] = k; 39 } 40 41 int ans = -0x7f7f7f7f,y; 42 for(int i = 1; i <= V; i++) 43 if(ans < f[F][i]) 44 y = i, ans = f[F][i]; 45 46 int x = F; 47 std::vector<int> v; 48 for(; x >= 1; y = pre[x][y], x--) 49 v.push_back(y); 50 51 reverse(v.begin(), v.end()); 52 cout<<ans<<endl; 53 for(int i = 0; i < (int) v.size(); i++) 54 cout<<v[i]<<" "; 55 puts(""); 56 return 0; 57 }
(不要吐槽奇怪的read()函数,一开始没发现有负数,写的快读,wa了好几遍。。。)