• [GDOI2014]拯救莫莉斯 状压DP


    题面:

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

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

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

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

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

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

    n * m <= 50, m < n

    简单题意:

    有一个带权值的矩阵,取一个方格的代价为它的权值,取一个方格时可以给它自己和上下左右的格子打上标记,求最小代价(代价相同取最少数量的方格)使得整个矩阵都被标记。

    题解:

    观察到n * m <=50,m < n,也就是说m最大也只能是7,看见这么小的数,,,显然这就是个状压啊!

    第一眼貌似就是很套路的状压,,,

    不过貌似还是有不同的,

    唯一的不同在于:一行受上一行和下一行的同时影响,也就是说当前行可以不满足全都标记,因为后来还可以有别的城市来标记它,

    因此枚举i和i-1和i-2行的状态,合法条件为:必须使得i-1合法,因此i-1要是再不合法的话以后都不能合法了,,,

    同时将f初始化为极大值,这样的话就无需判断i-2是否合法,因为不合法的话将不会被更新,那么值就是inf,也就不会被当做决策了

    但是观察到我们并不方便临时计算每个状态的各种数据,因此我们先预处理一遍,处理出每一行的每一个状态对应的城市数和代价,

    分别记为num[i][j].num 和 num[i][j].cost

    设f[i][j][k].cost 为第i行状态为j,第i-1行状态为k的最小代价,f[i][j][k].num表示在最小代价的基础上的最少城市数,

    那么我们的合法条件显然为:if(((k | j | l | (k << 1) | (k >> 1)) & all) == all),其中k为i-1行,j为i行,l为i-2行,

    all为2 ^ m - 1 , 也就是2进制下的 111111(m个1)

    & all用于消除高于m位的1,以免对ans产生影响。

    如果==all就表示合法,因为| j 和| l 表示用上下的城市来标记k一次,然后k << 1 和k >> 1就是用左右的城市来标记一次

    那么有转移方程:

    if(f[i-1][k][l] + num[i][j] <= f[i][j][k])

      f[i][j][k]=f[i-1][k][l] + num[i][j];

    其中

     1 node operator + (node a,node b)
     2 {
     3     a.cost += b.cost;
     4     a.num += b.num;
     5     return a;
     6 }
     7 
     8 bool operator <= (node a,node b)
     9 {
    10     if(a.cost < b.cost) return true;
    11     else if(a.cost == b.cost && a.num < b.num) return true;
    12     else return false;
    13 }

    最后统计ans的时候的合法条件为第n行合法。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define R register int
     4 #define AC 300
     5 #define inf 80000000
     6 #define ac 50
     7 int n,m,all;
     8 int s[ac][ac];
     9 struct node{
    10     int cost,num;
    11 }f[ac][AC][AC],num[ac][AC],ans;
    12 /*因为n * m <= 50 , m < n,所以m实际上是很小的,m <= sqrt(50),
    13 因此预处理出对于每一行,任意状态下的油库个数和代价,在代价相同的基础上取油库最少
    14 一定要注意状态从0开始枚举!*/
    15 node operator + (node a,node b)
    16 {
    17     a.cost += b.cost;
    18     a.num += b.num;
    19     return a;
    20 }
    21 
    22 bool operator <= (node a,node b)
    23 {
    24     if(a.cost < b.cost) return true;
    25     else if(a.cost == b.cost && a.num < b.num) return true;
    26     else return false;
    27 }
    28 
    29 void pre()
    30 {
    31     scanf("%d%d",&n,&m);
    32     all=(1 << m) - 1;
    33     for(R i=1;i<=n;i++)
    34         for(R j=1;j<=m;j++) 
    35             scanf("%d",&s[i][j]);
    36     for(R i=1;i<=n;i++)//枚举行,看做常数?
    37         for(R j=0;j<=all;j++)//枚举状态
    38             for(R k=1;k<=m;k++)//看做常数?
    39                 if(j & (1 << (m - k))) //预处理,,,但是看上去复杂度很高啊
    40                 {
    41                     num[i][j].cost+=s[i][k];
    42                     num[i][j].num++;
    43                 }
    44     ans.cost=inf , ans.num=inf;
    45 }
    46 
    47 void work()
    48 {
    49     for(R i=0;i<=all;i++) f[1][i][0]=num[1][i];
    50     for(R i=0;i<=all;i++)
    51         for(R j=0;j<=all;j++)
    52         {
    53             f[2][i][j].cost = inf;
    54             if(((j | i | (j << 1) | (j >> 1)) & all) == all) 
    55                 f[2][i][j]=f[1][j][0] + num[2][i];
    56         }
    57     for(R i=3;i<=n;i++)//枚举行
    58         for(R j=0;j<=all;j++)//枚举当前行
    59             for(R k=0;k<=all;k++)//枚举上一行状态
    60             {
    61                 f[i][j][k].cost = inf;
    62                 f[i][j][k].num = inf;
    63                 for(R l=0;l<=all;l++)//枚举上上行状态
    64                     if(((k | j | l | (k << 1) | (k >> 1)) & all) == all)//全都更新一遍,至于如何解决二次传播的方法,,,用原版就好了啊    
    65                         if(f[i-1][k][l] +  num[i][j] <= f[i][j][k])
    66                             f[i][j][k]=f[i-1][k][l] + num[i][j];                
    67             }
    68     for(R i=0;i<=all;i++)
    69     {
    70         for(R j=0;j<=all;j++)
    71             if(((i | j | (i << 1) | (i >> 1)) & all) == all)
    72                 if(f[n][i][j] <= ans) ans=f[n][i][j];
    73     }
    74     printf("%d %d
    ",ans.num,ans.cost);
    75 }//因为受两行影响,而且是上下两行,,,,所以当前行不用满足,保证上一行满足即可???
    76 //因为上一行还不满足的话就满足不了了                
    77 int main()
    78 {
    79 //    freopen("in.in","r",stdin);
    80     pre();
    81     work();
    82 //    fclose(stdin);
    83     return 0;
    84 }
  • 相关阅读:
    Aop 打印参数日志时,出现参数序列化异常。It is illegal to call this method if the current request is not in asynchron
    Nginx 配置反向代理ip
    springboot 的启动流程
    sqlserver 查看表死锁
    idea 中 下载源码:Sources not download for:
    sqlserver exists 与 in 的区别
    sqlserver case when 的使用方法
    Java项目启动时执行指定方法的几种方式
    使用多线程并获取执行结果
    nodejs实现一个文件存储服务
  • 原文地址:https://www.cnblogs.com/ww3113306/p/9097349.html
Copyright © 2020-2023  润新知