1、hdu 2126 Buy the souvenirs
题意:给出若干个纪念品的价格,求在能购买的纪念品的数目最大的情况下的购买方案。
思路:01背包+记录方案。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 using namespace std; 5 const int maxn = 35; 6 const int maxw = 510; 7 int cnt[maxw]; 8 int dp[maxw]; 9 int p[maxn]; 10 int main() 11 { 12 int t; 13 scanf("%d", &t); 14 while (t--) 15 { 16 int n, m; 17 scanf("%d%d", &n, &m); 18 for (int i = 0; i < n; i++) scanf("%d", p + i); 19 memset(dp, 0, sizeof(dp)); 20 memset(cnt, 0, sizeof(cnt)); 21 for (int i = 0; i < n; i++) 22 { 23 for (int v = m; v >= p[i]; v--) 24 { 25 if (dp[v - p[i]]+1 > dp[v]) 26 { 27 dp[v] = dp[v - p[i]]+1; 28 cnt[v]=cnt[v-p[i]]==0?1:cnt[v-p[i]]; 29 } 30 else if (dp[v - p[i]] + 1 == dp[v]) 31 { 32 cnt[v] += cnt[v - p[i]]==0?1:cnt[v-p[i]]; 33 } 34 } 35 } 36 if (dp[m] != 0)printf("You have %d selection(s) to buy with %d kind(s) of souvenirs. ", cnt[m], dp[m]); 37 else printf("Sorry, you can't buy anything. "); 38 } 39 return 0; 40 }
2、hdu 4281 Judges' response(状态压缩+01背包+MTSP问题)
题意:有n-1个参赛者,给出裁判和n-1个人的位置坐标、裁判和n-1个人的提问时间(裁判为0)。裁判有最长回答时间。
问:
①最少需要多少个裁判?
②当裁判数目无穷多时,问从初始点出发去解答参赛者提问后回到初始点的走过的最少距离。
思路:第一问可以用状态压缩+01背包解决;第二问MTSP问题
1 #include<iostream> 2 #include<algorithm> 3 #include<cstdio> 4 #include<cstring> 5 #include<cmath> 6 7 using namespace std; 8 const int maxn = 16; 9 const int maxm = 100010; 10 const int INF = 0x3f3f3f3f; 11 int n, m,maxtot; 12 int mp[maxn][maxn];//记录两两之间距离 13 int st[1 << maxn];//记录可行状态 14 bool ok[1 << maxn];//标记可行状态 15 int dp[1 << maxn];//记录当前状态下所需最少的裁判 16 int x[maxn],y[maxn];//记录坐标 17 int w[maxn];//记录提问时间 18 int tdis[maxn][1 << maxn];//tdis[i][j]表示在j状态下最后停在i处时的最小花费 19 int mdis[1 << maxn];//表示i这种状态下回到起点的最小花费 20 21 22 bool check(int cst) 23 { 24 int sum = 0; 25 for (int i = 0; i < n; i++) 26 { 27 if (cst&(1 << i)) sum += w[i]; 28 } 29 return sum <= m; 30 } 31 32 void Init() 33 { 34 for (int i = 0; i < n; i++) 35 { 36 for (int j = 0; j < n; j++) 37 { 38 mp[i][j] = ceil(sqrt(1.0*(x[i] - x[j])*(x[i] - x[j]) + (y[i] - y[j])*(y[i] - y[j]))); 39 } 40 } 41 memset(tdis, INF, sizeof(tdis)); 42 memset(mdis, INF, sizeof(mdis)); 43 tdis[0][1] = 0;//从起点到起点距离为0 44 } 45 46 int MTSP() 47 { 48 //得到每一条可行路径的最优解 49 for (int i = 1; i <= maxtot; i++) 50 { 51 if (ok[i]) 52 { 53 for (int j = 0; j < n; j++) 54 { 55 if (i&(1 << j)) 56 { 57 mdis[i] = min(mdis[i], tdis[j][i]+mp[j][0]);//i这种状态最后回到原点0的最小值={min(tdis[j][i]+mp[j][0])|0<=j<n} 58 for (int k = 0; k < n; k++) 59 { 60 if ((i&(1 << k))==0&&ok[i|(1<<k)])//如果i这种状态到不了位置k,则更新i|(1<<k)状态下到k处的最短距离 61 { 62 tdis[k][i | (1 << k)] = min(tdis[k][i | (1 << k)],tdis[j][i]+mp[j][k]); 63 } 64 } 65 } 66 } 67 } 68 } 69 //合并操作 70 for (int i = 0; i <= maxtot; i++) 71 { 72 if (i & 1)//如果回到原点(不要求ok[i],因为即使一个人不能完成,多个人一起可以完成) 73 { 74 for (int j = (i - 1)&i; j; j = (j - 1)&i)//枚举子集 75 { 76 mdis[i] = min(mdis[i], mdis[j | 1] + mdis[(i - j) | 1]); 77 } 78 } 79 } 80 return mdis[maxtot]; 81 } 82 83 84 int main() 85 { 86 while (~scanf("%d%d", &n, &m)) 87 { 88 for (int i = 0; i < n; i++) scanf("%d%d", x + i, y + i); 89 for (int i = 0; i < n; i++) scanf("%d", w + i); 90 maxtot = (1 << n) - 1; 91 int tot = 0; 92 memset(ok, 0, sizeof(ok)); 93 for (int i = 1; i <= maxtot; i++) 94 { 95 if (check(i)) 96 { 97 ok[i] = true; 98 st[tot++] = i; 99 } 100 } 101 //dp求解最小裁判数目 102 memset(dp, INF, sizeof(dp)); 103 dp[0] = 0; 104 for (int i = 0; i < tot; i++) 105 { 106 for (int j = maxtot; j >= 0; j--) 107 { 108 if (dp[j] != INF && (j&st[i]) == 0) 109 { 110 dp[st[i] | j] = min(dp[j] + 1, dp[st[i] | j]); 111 } 112 } 113 } 114 int ans1 = 0, ans2 = 0; 115 if (dp[maxtot] == INF) 116 { 117 ans1 = ans2 = -1; 118 } 119 else 120 { 121 ans1 = dp[maxtot]; 122 //求解多旅行商(MTSP)问题 123 Init(); 124 ans2 = MTSP(); 125 } 126 printf("%d %d ", ans1, ans2); 127 } 128 return 0; 129 }
3、uva 674 Coin Change
题意:有5中面值的硬币,需要凑成n元,有多少种方案。
思路:完全背包+记录方案数
1 #include<iostream> 2 #include<cstring> 3 using namespace std; 4 const int maxn = 8000; 5 int dp[maxn]; 6 int cnt[maxn]; 7 int v[] = {1, 5, 10, 25, 50}; 8 int main() 9 { 10 int n; 11 while (~scanf("%d", &n)) 12 { 13 memset(dp, 0, sizeof(dp)); 14 memset(cnt, 0, sizeof(cnt)); 15 cnt[0] = 1; 16 for (int i = 0; i < 5; i++) 17 { 18 for (int tv = v[i]; tv <= n; tv++) 19 { 20 if (dp[tv] == dp[tv - v[i]] + v[i]) cnt[tv] += cnt[tv - v[i]]; 21 else if (dp[tv] < dp[tv - v[i]] + v[i]) 22 { 23 dp[tv] = dp[tv - v[i]] + v[i]; 24 cnt[tv] = cnt[tv - v[i]]; 25 } 26 } 27 } 28 printf("%d ", cnt[n]); 29 } 30 return 0; 31 }
4、uva 147 Dollars
题意:有11中面值,求凑成n元的方案数
思路:先把n转换成整数,然后完全背包
1 #include<iostream> 2 #include<cstring> 3 using namespace std; 4 const int maxn = 30010; 5 int dp[maxn]; 6 long long cnt[maxn];//爆int 7 int v[] = { 10000,5000,2000,1000,500,200,100,50,20,10,5 }; 8 //另一种方法:用dp[i][j]表示用前i种硬币,组成j分的种类数, 9 //状态转移方程:dp[i][j] += DP(i - 1, j - k*d[i]) 10 int main() 11 { 12 double n; 13 while (~scanf("%lf", &n)) 14 { 15 int N = int(n * 100+0.5);//精度问题 16 if (N == 0)break; 17 memset(dp, 0, sizeof(dp)); 18 memset(cnt, 0, sizeof(cnt)); 19 cnt[0] = 1; 20 for (int i = 0; i < 11; i++) 21 { 22 for (int tv = v[i]; tv <= N; tv++) 23 { 24 if (dp[tv] == dp[tv - v[i]] + v[i]) cnt[tv] += cnt[tv - v[i]]; 25 else if (dp[tv] < dp[tv - v[i]] + v[i]) 26 { 27 dp[tv] = dp[tv - v[i]] + v[i]; 28 cnt[tv] = cnt[tv - v[i]]; 29 } 30 } 31 } 32 printf("%6.2lf%17lld ",n, cnt[N]); 33 } 34 return 0; 35 }
5、poj 3181 Dollar Dayz
题意:有1~k中面值,凑成n元的方案数。
思路:会爆long long,用大数+完全背包。
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 using namespace std; 5 const int maxn = 1010; 6 int dp[maxn]; 7 long long cnt1[maxn],cnt2[maxn]; 8 const long long MAX = 1e18; 9 int main() 10 { 11 int n,k; 12 while (~scanf("%d%d", &n,&k)) 13 { 14 memset(dp, 0, sizeof(dp)); 15 memset(cnt1, 0, sizeof(cnt2)); 16 memset(cnt2, 0, sizeof(cnt2)); 17 18 cnt1[0] = 1; 19 for (int i = 1; i <=k; i++) 20 { 21 for (int tv = i; tv <= n; tv++) 22 { 23 if (dp[tv] == dp[tv - i] + i) 24 { 25 cnt2[tv] +=cnt2[tv-i]+(cnt1[tv]+cnt1[tv - i])/MAX; 26 cnt1[tv] = (cnt1[tv] + cnt1[tv - i]) % MAX; 27 } 28 else if (dp[tv] < dp[tv -i] + i) 29 { 30 dp[tv] = dp[tv - i] + i; 31 cnt1[tv] = cnt1[tv - i]; 32 cnt2[tv] = cnt2[tv - i]; 33 } 34 } 35 } 36 if (cnt2[n])printf("%lld%lld ", cnt2[n], cnt1[n]); 37 else printf("%lld ", cnt1[n]); 38 } 39 return 0; 40 }
6、poj 3260 The Fewest Coins
题意:有n种面值的硬币,一位农夫带着每种硬币各ci个去买价值为T的商品,售货员可以找零任意多个硬币。问,农夫交给售货员的硬币和售货员找回的硬币之和最小为多少?
思路:对农夫多重背包(二进制优化),对售货员完全背包,找当前价格tv下的最小硬币数目。确定上限:前者为T+max(vi)*max(vi);后者为max(vi)*max(vi)。(鸽巢原理)(假设存在一种最优支付方案,给了多于t + max_v * max_v的钱,那么商店就会找回多于max_v * max_v的钱,这些硬币的个数大于max_v。设这些硬币的面值分别为a_i,根据鸽笼原理的应用,硬币序列中存在至少两个子序列,这两个子序列的和分别都能被max_v整除。如果我们直接用长度更小的那个子序列换算为面值为max_v的硬币某整数个,再去替换母序列就能用更少的硬币买到商品,形成矛盾。)
鸽巢原理(抽屉原理或狄利克雷原理)的应用:
1 #include<iostream> 2 #include<algorithm> 3 #include<cstdio> 4 #include<cstring> 5 6 using namespace std; 7 8 const int maxv1 = 10000 + 120 * 120 + 10; 9 const int maxv2 = 120 * 120 + 10; 10 const int maxn = 110; 11 const int INF = 0x3f3f3f3f; 12 int val[maxn]; 13 int num[maxn]; 14 int n, t; 15 int dp1[maxv1]; 16 int dp2[maxv2]; 17 int main() 18 { 19 while (~scanf("%d%d", &n, &t)) 20 { 21 int maxv = 0; 22 for (int i = 0; i < n; i++) scanf("%d", val + i),maxv=max(maxv,val[i]); 23 for (int i = 0; i < n; i++) scanf("%d", num + i); 24 //农夫多重背包,找tv下最小硬币数 25 int maxsum = t + maxv*maxv; 26 memset(dp1, INF, sizeof(dp1)); 27 dp1[0] = 0; 28 for (int i = 0; i < n; i++) 29 { 30 int k = 1; 31 bool flag = true; 32 while (flag) 33 { 34 if (k>num[i]) 35 { 36 k = num[i] - k / 2; 37 flag = false; 38 } 39 for (int tv = maxsum; tv >= k*val[i]; tv--) 40 { 41 dp1[tv] = min(dp1[tv], dp1[tv - k*val[i]] + k); 42 } 43 k *= 2; 44 } 45 } 46 //销售员完全背包,找tv下最少硬币数 47 int maxsum2 = maxv*maxv; 48 memset(dp2, INF, sizeof(dp2)); 49 dp2[0] = 0; 50 for (int i = 0; i < n; i++) 51 { 52 for (int j = val[i]; j <= maxsum2; j++) 53 { 54 dp2[j] = min(dp2[j], dp2[j - val[i]] + 1); 55 } 56 } 57 58 int ans = INF; 59 for (int i = t; i <= maxsum; i++) ans = min(ans, dp1[i] + dp2[i - t]); 60 if(ans!=INF)printf("%d ", ans); 61 else printf("-1 "); 62 } 63 return 0; 64 }
7、poj 2063 Investment
题意:有若干基金可以选择,初始有一定的资金,问过一定年数后最大的本息?
思路:对每一年用完全背包求当前本金下所能获得的最多利润,累加本息。由于题目保证本金和基金的价格都是1000的倍数,dp时可以/1000.
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 6 using namespace std; 7 8 const int maxv = 50100; 9 const int maxn = 15; 10 int val[maxn]; 11 int itst[maxn]; 12 int dp[maxv]; 13 14 int main() 15 { 16 int t; 17 scanf("%d", &t); 18 while (t--) 19 { 20 int st, year; 21 scanf("%d%d", &st, &year); 22 int n; 23 scanf("%d", &n); 24 for (int i = 0; i < n; i++) scanf("%d%d", val + i, itst + i),val[i]/=1000; 25 int ans = st; 26 memset(dp, 0, sizeof(dp)); 27 for (int i = 0; i < year; i++) 28 { 29 int tot = ans / 1000; 30 for (int j = 0; j < n; j++) 31 { 32 for (int tv = val[j]; tv <= tot; tv++) 33 { 34 dp[tv] = max(dp[tv], dp[tv - val[j]] + itst[j]); 35 } 36 } 37 ans += dp[tot]; 38 } 39 printf("%d ", ans); 40 } 41 return 0; 42 }
8、zoj 3623 Battle Ships
题意:有一个塔楼,有L点血。有n种船,每时刻在建造的船最多只有一艘。问最少的时间。
思路:dp[i]表示前i秒所能造成的最大伤害。dp[i+cost[j]]=max(dp[i+cost[j]],dp[i]+i*dps[j]).可以这么理解,在第i+1到i+cost[j]内建造船j,有i秒可以让其输出。
1 #include<cstdio> 2 #include<iostream> 3 #include<algorithm> 4 #include<cstring> 5 using namespace std; 6 7 const int maxt = 350; 8 const int maxn = 35; 9 int dp[maxt];//dp[i]表示前i秒能造成的最大伤害 10 int dps[maxn]; 11 int cost[maxn]; 12 13 int main() 14 { 15 int n, l; 16 while (~scanf("%d%d", &n, &l)) 17 { 18 for (int i = 0; i < n; i++) scanf("%d%d", cost + i, dps + i); 19 memset(dp, 0, sizeof(dp)); 20 int ans = maxt; 21 for (int i = 0; i < n; i++) 22 { 23 for (int j =0; j < maxt-cost[i]; j++) 24 { 25 dp[j + cost[i]] = max(dp[j + cost[i]], dp[j] + j*dps[i]); 26 if (dp[j + cost[i]] >= l) ans = min(ans, j + cost[i]); 27 } 28 } 29 printf("%d ", ans); 30 } 31 return 0; 32 }
9、zoj 3524 Crazy Shopping
题意:有一个有向无环图,图上有n个点,每个地点有一种占用V空间,价值为W的物品,现在一个人从某点出发,如果背包有C重量的物品,走过路程为K,会消耗C*K的体力,那么在背包容量一定的情况下,我要得到最大的价值需要消耗的最少体力是多少。
思路:拓扑排序+完全背包+记录耗能
1 #include<iostream> 2 #include<vector> 3 #include<queue> 4 #include<algorithm> 5 #include<cstdio> 6 #include<cstring> 7 using namespace std; 8 9 int N, M, W,X;//景点数目、道路条数、背包容量、起始点 10 const int maxn = 610; 11 const int maxm = 60010; 12 const int maxw = 2010; 13 14 long long dp[maxn][maxw],mcost[maxn][maxw];//[i][j],表示走到i时,背包装有质量为j的最大价值及其最小耗能 15 16 struct node 17 { 18 int to, len; 19 node(int tt=0,int ll=0):to(tt),len(ll){ } 20 }; 21 vector<node>mp[maxn]; 22 23 int tw[maxn], tv[maxn];//重量及其价值 24 25 int TPOrder[maxn];//记录拓扑排序 26 bool ok[maxn];//表示能否从起点X到达该点 27 int indgree[maxn];//记录入度 28 29 void Init() 30 { 31 X--; 32 for (int i = 0; i <= N; i++) mp[i].clear(); 33 memset(ok, 0, sizeof(ok)); 34 memset(dp, 0, sizeof(dp)); 35 memset(mcost, -1, sizeof(mcost)); 36 memset(indgree, 0, sizeof(indgree)); 37 } 38 39 void GetWV() 40 { 41 for (int i = 0; i < N; i++) scanf("%d%d", tw + i, tv + i); 42 } 43 44 void GetMP() 45 { 46 for (int i = 0; i < M; i++) 47 { 48 int from, to, len; 49 scanf("%d%d%d", &from, &to, &len); 50 from--, to--; 51 mp[from].push_back(node(to, len));//有向边 52 indgree[to]++; 53 } 54 } 55 56 void GetTP() 57 { 58 queue<int>q; 59 int index = 0; 60 for (int i = 0; i < N; i++) if (indgree[i] == 0) q.push(i); 61 while (!q.empty()) 62 { 63 int u = q.front(); 64 q.pop(); 65 TPOrder[index++] = u; 66 int sz = mp[u].size(); 67 for (int i = 0; i < sz; i++) 68 { 69 int v = mp[u][i].to; 70 if (indgree[v]) 71 { 72 indgree[v]--; 73 if (indgree[v] == 0) q.push(v); 74 } 75 } 76 } 77 } 78 79 long long Solve() 80 { 81 long long maxv = 0, mindis = 0; 82 for (int i = 0; i <= W; i++) mcost[X][i] = 0; 83 for (int w = tw[X]; w <= W; w++) 84 { 85 dp[X][w] = max(dp[X][w], dp[X][w - tw[X]] + tv[X]); 86 if (dp[X][w] > maxv) maxv = dp[X][w], mindis = 0; 87 } 88 ok[X] = true; 89 for (int j = 0; j < N; j++) 90 { 91 int u = TPOrder[j]; 92 if (!ok[u])continue; 93 int sz = mp[u].size(); 94 for (int k = 0; k < sz; k++) 95 { 96 int v = mp[u][k].to, len = mp[u][k].len; 97 ok[v] = true; 98 //先到达 99 for (int i = 0; i <= W; i++) 100 { 101 if (dp[v][i] < dp[u][i]) 102 { 103 dp[v][i] = dp[u][i]; 104 mcost[v][i] = mcost[u][i] + len*i; 105 } 106 else if (dp[v][i] == dp[u][i]) 107 { 108 if (mcost[v][i] == -1) mcost[v][i] = mcost[u][i] + len*i; 109 else mcost[v][i] = min(mcost[v][i], mcost[u][i] + len*i); 110 } 111 if (i>0&&dp[v][i] == dp[v][i - 1]) mcost[v][i] = min(mcost[v][i], mcost[v][i - 1]); 112 } 113 //再购买 114 for (int i = tw[v]; i <= W; i++) 115 { 116 if (dp[v][i] < dp[v][i - tw[v]] + tv[v]) 117 { 118 dp[v][i] = dp[v][i - tw[v]] + tv[v]; 119 mcost[v][i] = mcost[v][i - tw[v]]; 120 } 121 else if (dp[v][i] == dp[v][i - tw[v]] + tv[v]) mcost[v][i] = min(mcost[v][i], mcost[v][i - tw[v]]); 122 } 123 //更新ans 124 for (int i = 0; i <= W; i++) 125 { 126 if (dp[v][i] > maxv) maxv = dp[v][i], mindis = mcost[v][i]; 127 else if (dp[v][i] == maxv) mindis = min(mindis, mcost[v][i]); 128 } 129 } 130 } 131 return mindis; 132 } 133 134 int main() 135 { 136 while (~scanf("%d%d%d%d", &N, &M, &W, &X)) 137 { 138 Init(); 139 GetWV(); 140 GetMP(); 141 GetTP();//拓扑排序 142 long long ans=Solve(); 143 printf("%lld ", ans); 144 } 145 return 0; 146 }
10、zoj 3662 Math Magic
题意:定义k个数和为N,k个数的最小公倍数为M,求k个数的方案?
思路:滚动数组dp[now][j][k]表示选当前个数的数,和为j,最小公倍数为k的方案数,dp[now][sv + val][LCM[tlcm][ind]] = dp[now][sv + val][LCM[tlcm][ind]] + dp[now ^ 1][sv][tlcm]。所选择的数必须是M的因子。
1 #include<iostream> 2 #include<cmath> 3 #include<cstring> 4 #include<cstdio> 5 using namespace std; 6 int N, M, K; 7 const int maxn = 1010; 8 const int maxm = 1010; 9 const int maxk = 110; 10 const int maxfac = 40; 11 const int MOD = 1e9 + 7; 12 int factor[maxm]; 13 int pos[maxm]; 14 int LCM[maxm][maxm]; 15 16 int dp[2][maxn][maxfac];//滚动数组dp[now][j][k]表示选当前个数的数,和为j,最小公倍数为k的方案数 17 18 int Gcd(int a, int b) 19 { 20 if (a < b) a = a^b, b = a^b, a = a^b; 21 while (a%b) 22 { 23 int t = a % b; 24 a = b; 25 b = t; 26 } 27 return b; 28 } 29 int Lcm(int a, int b) 30 { 31 return a*b / Gcd(a, b); 32 } 33 int main() 34 { 35 while (~scanf("%d%d%d", &N, &M, &K)) 36 { 37 int cnt = 0; 38 //找到所有因子(1000以内因子最多只有32个) 39 for (int i = 1; i <= M; i++) 40 { 41 if (M%i == 0) factor[++cnt] = i,pos[i]=cnt; 42 } 43 //得到所有因子两两的最小公倍数(该最小公倍数一定是其他某个因子) 44 memset(LCM, 0, sizeof(LCM)); 45 for (int i = 1; i <= cnt; i++) 46 { 47 for (int j = i; j <= cnt; j++) 48 { 49 int lcm = Lcm(factor[i], factor[j]); 50 LCM[i][j] = LCM[j][i] = pos[lcm]; 51 } 52 } 53 //dp-完全背包 54 memset(dp, 0, sizeof(dp)); 55 int now = 0; 56 for (int i = 1; i <= cnt; i++) dp[now][factor[i]][i] = 1; 57 for (int t = 2; t <= K; t++) 58 {//选第t个数 59 now ^= 1; 60 memset(dp[now], 0, sizeof(dp[now]));//注意清零 61 for (int sv = t - 1; sv <= N; sv++) 62 {//和为sv 63 for (int tlcm = 1; tlcm <= cnt; tlcm++) 64 {//前一层的LCM 65 if (dp[now ^ 1][sv][tlcm] == 0) continue; 66 for (int ind = 1; ind <= cnt; ind++) 67 {//枚举所加的数 68 69 int val = factor[ind]; 70 if (sv + val > N)continue; 71 dp[now][sv + val][LCM[tlcm][ind]] = (dp[now][sv + val][LCM[tlcm][ind]] + dp[now ^ 1][sv][tlcm]) % MOD; 72 } 73 } 74 } 75 } 76 printf("%d ", dp[now][N][cnt]); 77 } 78 return 0; 79 }
11、zoj 3956 Course Selection System
题意:有n门课程,每门课有Hi、Ci值。现在需要选若干门课程,使得SumH^2-SumH*SumC-SumC^2值最大。
思路:01背包,把C看成体积,H看成价值。当C固定时,求出最大的SumH.最后遍历dp数组得到最大值(如果一个i值不能有Ci值组成,其SumH肯定和小于i的某一个值相同,而那个值的算式结果肯定比其大)
1 #include<iostream> 2 #include<algorithm> 3 #include<cstdio> 4 #include<cstring> 5 using namespace std; 6 const int MAXC = 50010; 7 long long dp[MAXC]; 8 const long long INF = 0x3f3f3f3f3f3f3f3f; 9 const int maxn = 510; 10 int h[maxn], c[maxn]; 11 int main() 12 { 13 int t; 14 scanf("%d", &t); 15 while (t--) 16 { 17 int n; 18 scanf("%d", &n); 19 int sumc = 0; 20 for (int i = 0; i < n; i++) scanf("%d%d", h + i, c + i),sumc+=c[i]; 21 memset(dp, 0, sizeof(dp)); 22 long long ans = 0; 23 for (int i = 0; i < n; i++) 24 { 25 for (int j = sumc; j >= c[i]; j--) 26 { 27 dp[j] = max(dp[j], dp[j - c[i]] + h[i]); 28 } 29 } 30 for (int i = 0; i <= sumc; i++) ans = max(ans, 1ll*dp[i] * dp[i] - dp[i] * i - i * i); 31 printf("%lld ", ans); 32 } 33 return 0; 34 }