zoukankan      html  css  js  c++  java
  • GDOI2014 拯救莫莉斯

    问题描述

    莫莉斯·乔是圣域里一个叱咤风云的人物,他凭借着自身超强的经济头脑,牢牢控制了圣域的石油市场。

    圣域的地图可以看成是一个n*m的矩阵。每个整数坐标点(x , y)表示一座城市(1<=x<= n, 1<=y<=m)。两座城市间相邻的定义为:对于城市(Ax, Ay)和城市(Bx, By),满足(Ax - Bx)2 + (Ay - By)2 = 1。

    由于圣域的石油贸易总量很大,莫莉斯意识到不能让每笔石油订购单都从同一个油库里发货。为了提高效率,莫莉斯·乔决定在其中一些城市里建造油库,最终使得每一个城市X都满足下列条件之一:

    1.该城市X内建有油库,

    2.某城市Y内建有油库,且城市X与城市Y相邻。

    与地球类似,圣域里不同城市间的地价可能也会有所不同,所以莫莉斯想让完成目标的总花费尽可能少。如果存在多组方案,为了方便管理,莫莉斯会选择建造较少的油库个数。

    输入格式

    第一行两个正整数n,m ( n * m <= 50 且m<=n),表示矩阵的大小。

    接下来一个n行m列的矩阵F,Fi, j表示在城市(i,j)建造油库的代价。

    输出格式

    输出两个数,建造方案的油库个数和方案的总代价。

    输入样例:

    输出样例:

    3 3

    6 5 4

    1 2 3

    7 8 9

    3 6

    数据范围

    对于30%数据满足 n * m <= 25;

    对于100%数据满足n * m <= 50; 0 <= Fi, j <= 100000

      这道题再次教给我们一个道理,暴力出奇迹。

      当时根据“经验”——考试必有爆搜,果断当搜索题干(其实也是因为看成了n≤50,m≤50,没想到状压),按照之前“翻格子”的思路,挨行搜索,每到一行行末检测上一行是否全部被覆盖,否直接return ,知道最后一行,中间通过若当前解小于当前最优解,直接返回,先搜油库少的情况后搜油库多的情况,等等等等一系列通用剪枝,然后用rand()出来的数据一测,大约在n*m=35左右就会T掉。其实,我的暴力算法和测试数据本身有着极大的关系,也就是说数据从某种意义上讲决定了我的算法复杂度,比如从我自己造的数据来看2*25绝对没有十几分钟出不来,但最后还真过了,所以最后只有1*50的数据T成狗,(不是很理解官方题解说的30~60分)其他貌似速度还是挺不错的。 


      淡扯完了,现在是正解。

      由m<=n可知,m最大才为7,因此果断状压,让我们先预估一下复杂度大概是多少,2^7是128,因此当m=n=7时该数最大,那么最大时所允许的复杂度多少呢?n*2^(3*m)中的n都可以当一个较大的常数算了,又因为本题中对每一行是否被完全覆盖与他自己和上下行都有关,正好是3行,因此,凭借职业敏感和推断状压应当是上中下3行,当然注意边界。那么我们就先顺着这个思路向下走,如果开三维状压的话是2097152,完全够用,但真的用开三维吗,显然不用,因为下一行的状态由本行转移,在转移的时候判断一下能否转移一下即可,那么数组大小就变为了16324,貌似小了一点啊,但别忘了,还有行,因此就为816200,这就差不多了,既然状压两维,行一维,那么它表示的意义也就清楚了f[i][j][k],就表示第i行,上一行(买油库)的状态为j,本行买油库的状态为k,且前i-1行全部被覆盖的解的最优值,至于要开几座油库,再存一个相同的g数组表示即可。

      转移方程是比较有意思的,不难想,但要注意优先级:

      

      至于最终答案嘛,就是f[n+1][x][0]中的最小值,x任意。最后直接输出它和与它对应的g数组即可,注意一下顺序。

     1 #include<iostream>
     2 #include<cstdlib>
     3 #include<cstdio>
     4 #include<cstring>
     5 #include<algorithm>
     6 #include<cmath>
     7 #include<map>
     8 using namespace std;
     9 int n,m,a[55][55];
    10 int f[55][1<<8][1<<8];
    11 int g[55][1<<8][1<<8];
    12 int main(){
    13     scanf("%d%d",&n,&m);
    14     for(int i=1;i<=n;i++)
    15     {
    16         for(int j=1;j<=m;j++)
    17         {
    18             scanf("%d",&a[i][j]);
    19         }
    20     }
    21     memset(f,0x7f,sizeof(f));
    22     memset(g,0x3f,sizeof(g));
    23     for(int i=0;i<(1<<m);i++)
    24     {
    25         int now=0;
    26         f[1][0][i]=g[1][0][i]=0;
    27         for(int k=1;;k++)
    28         {
    29             if((1<<(k-1))&i)
    30             {
    31                 now|=(1<<(k-1));
    32                 f[1][0][i]+=a[1][k];
    33                 g[1][0][i]++;
    34             }
    35             if(now>=i)
    36                 break;
    37         }
    38     }
    39     for(int i=1;i<=n;i++)
    40     {
    41         for(int j=0;j<(1<<m);j++)
    42         {
    43             for(int k=0;k<(1<<m);k++)
    44             {
    45                 for(int l=0;l<(1<<m);l++)
    46                 {
    47                     int now=0,sum=0,js=0;
    48                     for(int t=1;;t++)
    49                     {
    50                         if((1<<(t-1))&l)
    51                         {
    52                             js++;
    53                             now|=(1<<(t-1));
    54                             sum+=a[i+1][t];
    55                         }
    56                         if(now>=l)
    57                             break;
    58                     }
    59                     if(((l|j|k|(k>>1)|(k<<1))&((1<<m)-1))==((1<<m)-1))
    60                     {
    61                         if((f[i+1][k][l]>f[i][j][k]+sum)||(f[i][j][k]+sum==f[i+1][k][l]&&g[i+1][k][l]>g[i][j][k]+js))
    62                         {
    63                             f[i+1][k][l]=f[i][j][k]+sum;
    64                             g[i+1][k][l]=g[i][j][k]+js;
    65                         }
    66                     }
    67                 }
    68             }
    69         }
    70     }
    71     int ans=0x7fffffff,sum=0x7fffffff;
    72     for(int i=0;i<(1<<m);i++)
    73     {
    74         if(ans>f[n+1][i][0]||(ans==f[n+1][i][0]&&sum>g[n+1][i][0]))
    75         {
    76             ans=f[n+1][i][0];
    77             sum=g[n+1][i][0];
    78         }
    79     }
    80     printf("%d %d
    ",sum,ans);
    81     //while(1);
    82     return 0;
    83 }
    View Code
  • 相关阅读:
    第2章 NIO入门
    Docker Compose命令详解
    网络层相关术语解释
    linux查看并发连接数
    带宽计算方法
    ELK日志分析平台搭建
    mysql查找json格式列的指定字段值
    Oracle性能优化
    修改hosts文件不需要重启的方法
    freeswitch的internal的profile无法启动
  • 原文地址:https://www.cnblogs.com/liutianrui/p/7295977.html
Copyright © 2011-2022 走看看