此題出自 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了好幾遍。。。)
本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。