zoukankan      html  css  js  c++  java
  • poj 1015 Jury Compromise

    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也放进去了,所以看起来会比较长)

    View Code
      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

  • 相关阅读:
    在Ubuntu11.10中安装OpenCV2.3.1的详细步骤
    基于二元语法模型的中文分词
    相似图片搜索的原理
    基于GPU的KMeans聚类算法
    Windows下Eclipse和PyDev搭建完美Python开发环境
    Ubuntu 11.10+win7双系统启动项管理及配置方法
    主题爬虫
    vue定义全局过滤器
    element elimage 放多张图片,显示大图
    element UI的form 禁止浏览器自动填充用户名或密码
  • 原文地址:https://www.cnblogs.com/LyonLys/p/poj_1015_Lyon.html
Copyright © 2011-2022 走看看