http://poj.org/problem?id=1015
周日,结束了持续两个星期的个人赛,那是多么难以忘怀的一次选拔,成绩可谓差到谷底了...心情也是一样。晚上公布了个人赛的统计结果,庆幸的是训练依然继续,但是将会和谁组队,就只好看明天下午有哪位大牛会好心收留我了。恶心的结果,也是意料之内的...心情再怎么不好,还是应该为能够继续训练偷笑一下,缓解这一个星期的郁闷!
在个人赛中,出现很多种类型的题目....什么网络流,二分匹配,凸包,线段树(我打算在未来几天里将我前几天弄懂的线段树写一下),生成树计数...甚至出现了随机算法 快速分解质因数 (链接里是一个比较简明的Pollard-rho算法代码)。
晚上回到宿舍,在poj和hdu找了些dp的题目练了一下,这是其中一题。题意可以理解为,给出n种物品,求取其中m种,使得这m种物品的分别两种属性的和的差距(和的绝对值)最小。如果有多种情况,输出和最大的一种,并且要输出选择的物品的编号。
一开始用物品的两种属性进行dp,用boolean数组来判断能达到的位置,可是这时的时间复杂度是O(n*m*maxsum^2),刚开始算错数了,没发觉这个问题..交上去果断返回一个TLE。于是,我构思了一下,更改了dp的状态。因为想到,直接记录他们的差和和就可以推出原来的两个数。两者的差不取绝对值,因此有可能是负数,所以要进行数组的平移,最后构造出类似天平的一个结构。
dp的几个状态:
第一个状态——当前决策第i个物品(如果不是要backtrack出所用的物品,这个状态无需表达出来,可以直接用滚动数组实现)
第二个状态——当前选择了j个物品
第三个状态——选择j件物品后,两种属性的差值是k
dif —— 第一种属性减第二种的差值
sum —— 两种属性的和
状态转移方程是 dp[i][j][k] = max(dp[i - 1][j][k], dp[i - 1][j - 1][k - dif] + sum)
代码:(两部分都贴出来,用宏做开关....另外,我把debug也放进去了,所以看起来会比较长)
1 #include <cstdio> 2 #include <cstring> 3 #include <cmath> 4 5 #define debug 0 6 #define prog 1//1是较高效的代码 2是超时的代码 7 8 int min2(int _a, int _b){return _a < _b ? _a : _b;} 9 int max2(int _a, int _b){return _a > _b ? _a : _b;} 10 11 int s[20], top; 12 int d[201], p[201]; 13 #if prog == 1 14 15 int dp[201][21][1001]; 16 const int inf = 100000000; 17 const int mid = 500;//这里至少要420,这样下面的代码就不需要分类讨论了 18 19 int main(){ 20 int n, m; 21 int c = 1; 22 23 while (~scanf("%d%d", &n, &m) && (n || m)){ 24 for (int k = 0; k <= n; k++){ 25 for (int i = 0; i <= m; i++){ 26 for (int j = 0, endj = mid << 1; j <= endj; j++){ 27 dp[k][i][j] = -inf; 28 } 29 } 30 dp[k][0][mid] = 0; 31 } 32 for (int i = 1; i <= n; i++){ 33 scanf("%d%d", &d[i], &p[i]); 34 for (int j = 1, endj = min2(i, m); j <= endj; j++){ 35 int dif = d[i] - p[i]; 36 int sum = d[i] + p[i]; 37 for (int k = mid - 400, endk = mid + 400; k <= endk; k++){ 38 dp[i][j][k] = max2(dp[i - 1][j][k], dp[i - 1][j - 1][k - dif] + sum); 39 } 40 } 41 } 42 43 bool found = false; 44 int mi = 0, mj = 0; 45 46 for (int i = 0; i <= 400 && !found; i++){ 47 if (dp[n][m][mid + i] > 0){ 48 found = true; 49 mj = i; 50 mi = dp[n][m][mid + i]; 51 } 52 if (dp[n][m][mid - i] > 0){ 53 found = true; 54 if (mi < dp[n][m][mid - i]){ 55 mj = -i; 56 mi = dp[n][m][mid - i]; 57 } 58 } 59 } 60 #if debug 61 for (int i = 0; i <= m; i++){ 62 for (int j = mid - 5; j <= mid + 5; j++){ 63 printf("%d ", dp[n][i][j]); 64 } 65 puts(""); 66 } 67 printf("mi %d mj %d\n", mi, mj); 68 puts(""); 69 printf("%d %d\n", (mi + mj) >> 1, (mi - mj) >> 1); 70 #endif 71 printf("Jury #%d\nBest jury has value %d for prosecution and value %d for defence:\n", c, (mi + mj) >> 1, (mi - mj) >> 1); 72 int t = n; 73 top = 0; 74 mj += mid; 75 76 for (; t && mj && m; t--){//刚开始m忘记判断了,所以RE了几次,还以为是数组不够大搞到我不停开大数组 77 int dif = d[t] - p[t]; 78 int sum = d[t] + p[t]; 79 80 if (dp[t][m][mj] == dp[t - 1][m - 1][mj - dif] + sum){ 81 #if debug 82 puts("pass"); 83 #endif 84 m--; 85 mj -= dif; 86 s[top++] = t; 87 } 88 }//backtrack只要直接找到满足更新条件的两个数就可以了 89 c++; 90 for (int i = top - 1; i >= 0; i--){ 91 printf(" %d", s[i]); 92 } 93 puts(""); 94 puts(""); 95 } 96 97 return 0; 98 } 99 100 101 #endif 102 103 104 105 106 107 108 109 110 111 #if prog == 2 112 bool dp[21][401][401]; 113 114 int main(){ 115 int n, m; 116 int c = 1; 117 int sumd, sump; 118 119 while (~scanf("%d%d", &n, &m) && (n || m)){ 120 memset(dp, 0, sizeof(dp)); 121 dp[0][0][0] = true; 122 sumd = sump = 0; 123 for (int i = 1; i <= n; i++){ 124 scanf("%d%d", &d[i], &p[i]); 125 sumd += d[i]; 126 sumd = min2(sumd, 400); 127 sump += p[i]; 128 sump = min2(sump, 400); 129 for (int l = min2(i, m); l >= 1; l--){ 130 for (int j = sumd; j >= d[i]; j--){ 131 for (int k = sump; k >= p[i]; k--){ 132 dp[l][j][k] |= dp[l - 1][j - d[i]][k - p[i]]; 133 } 134 } 135 } 136 } 137 138 bool found = false; 139 int mi = 0, mj = 0; 140 141 for (int t = 0; t <= 400 && !found; t++){ 142 for (int i = 400; i >= t && !found; i--){ 143 if (dp[m][i][i - t]){ 144 found = true; 145 mi = i; 146 mj = i - t; 147 } 148 else if (dp[m][i - t][i]){ 149 found = true; 150 mi = i - t; 151 mj = i; 152 } 153 } 154 } 155 #if debug 156 for (int i = 0; i <= 20; i++){ 157 for (int j = 0; j <= 20; j++){ 158 printf("%d", dp[1][i][j]); 159 } 160 puts(""); 161 } 162 printf("i %d j %d\n", mi, mj); 163 #endif 164 165 printf("Jury #%d\nBest jury has value %d for prosecution and value %d for defence:\n", c, mi, mj); 166 c++; 167 top = 0; 168 while (mi && mj && m){ 169 for (int i = n; i >= 1; i--){ 170 if (mi - d[i] >= 0 && mj - p[i] >= 0 && dp[m - 1][mi - d[i]][mj - p[i]]){ 171 s[top++] = i; 172 mi -= d[i]; 173 mj -= p[i]; 174 m--; 175 } 176 } 177 } 178 for (int i = top - 1; i >= 0; i--){ 179 printf(" %d", s[i]); 180 } 181 puts(""); 182 } 183 184 return 0; 185 } 186 #endif
Work Harder!Work for My Better Life!
--Written by Lyon