題目鏈接: http://bestcoder.hdu.edu.cn/contests/contest_showproblem.php?cid=774&pid=1003
題目大意: a[i], b[i] | 0 <= i <= n .................... k[j], p[j] | 0 <= j <= m, n個魔王, m中法術方式, 每個法術攻擊p[j], 消耗k[j]水晶, 每個魔王a[i]生命, b[i]護甲, 攻擊必須先打穿護甲, 求殺死所有魔王的最少水晶數。
解題思路: dp[i][j] 表示一個魔王血量為i 護甲為 j 的時候, 殺死它需要的最少水晶, 哦對了, 水晶無限, 所以就轉換成n個完全背包問題, 再加和。
代碼: 下面我的代碼有錯, 不知道哪兒錯了

#include <iostream> #include <cstdio> #include <string> #include <vector> #include <map> #include <cstring> #include <iterator> #include <cmath> #include <algorithm> #include <stack> #include <deque> #include <map> #define lson l, m, rt<<1 #define rson m+1, r, rt<<1|1 const int maxn = 1e5+100; const int maxm = 1e3 + 100; const int maxa = 1e3 + 100; const int maxb = 15; using namespace std; const int INF = 0x3fffffff; int a[maxn]; int b[maxn]; int k[maxm]; int p[maxm]; int dp[maxa][maxb]; // dp(i, j)生命值為i , 防御值為 j 的時候消滅它花費最少 int main() { int n, m; while( scanf( "%d%d", &n, &m ) == 2 ) { int defend = 0; int attack = 0; int ma = 0; for( int i = 1; i <= n; i++ ) { scanf( "%d%d", a+i, b+i ); defend = max( defend, *(b+i) ); ma = max( ma, *(a+i) ); } for( int i = 1; i <= m; i++ ) { scanf( "%d%d", k+i, p+i ); attack = max( attack, *(p+i) ); } if( attack <= defend ) { printf( "-1\n" ); continue; } for( int i = 0; i <= ma; i++ ) { for( int j = 0; j <= defend; j++ ) { if( i == 0 ) dp[0][j] = 0; else dp[i][j] = INF; } } for( int i = 1; i <= ma; i++ ) { for( int j = 0; j <= defend; j++ ) { for( int t = 1; t <= m; t++ ) { if( j > p[t] ) continue; else if( i - (p[t]-j) >= 0 ) { dp[i][j] = min( dp[i][j], dp[i-(p[t]-j)][j]+k[t] ); } else dp[i][j] = min( dp[i][j], dp[0][j]+k[t] ); } } } long long res = 0; for( int i = 1; i <= n; i++ ) { res += dp[a[i]][b[i]]; } printf( "%lld\n", res ); } return 0; }
徐文棟的AC代碼:

#include <bits/stdc++.h> using namespace std; typedef long long LL; const int maxn = 100100; const int maxm = 1010; const int maxp = 10010; const int maxa = 1010; const int maxb = 11; const LL inf = (1LL << 60); int n, m; int a[maxn], b[maxn], k[maxm], p[maxm]; LL f[maxa][maxb]; signed main() { // freopen("in", "r", stdin); while(~scanf("%d %d",&n,&m)) { for(int i = 0; i < maxa; i++) { for(int j = 0; j < maxb; j++) { f[i][j] = inf; } } for(int i = 0; i < maxb; ++i) f[0][i] = 0; int ha = -1, hb = -1; for(int i = 1; i <= n; i++) { scanf("%d%d",&a[i], &b[i]); ha = max(ha, a[i]); hb = max(hb, b[i]); } for(int i = 1; i <= m; i++) scanf("%d%d",&k[i], &p[i]); for(int i = 1; i <= ha; i++) { for(int j = 0; j <= hb; j++) { for(int x = 1; x <= m; x++) { if(j >= p[x]) continue; if(i + j - p[x] > 0) f[i][j] = min(f[i][j], f[i+j-p[x]][j] + k[x]); else f[i][j] = min(f[i][j], f[0][j] + k[x]); } } } LL ret = 0; bool flag = 0; for(int i = 1; i <= n; i++) { if(f[a[i]][b[i]] == inf) { flag = 1; break; } ret += f[a[i]][b[i]]; } if(flag) puts("-1"); else printf("%I64d\n", ret); } return 0; } Close BestCoder Contest System 2.0
思考: 一開始我想的狀態是前i個魔王再加上一個狀態來求解, 太天真, 每一個魔王都是獨立的, 所以dp方程不管怎么都沒有辦法轉移 ,很難受, 然后我想是不是應該像《勁歌金曲》那樣不關心前面的狀態, 用滾動數組d[i], i表示血量, 同樣面臨着無法轉移的問題, 后來問了人才知道, 事先預處理每個魔王, 再加和, 自己好蠢。 其實不是蠢的問題, 我想問題就是自己還是沒有理清思路, 不能夠找到真正和結果相聯系的狀態, 還得多做題。