zoukankan      html  css  js  c++  java
  • [NOI2001]炮兵阵地 状压DP

    题面:

    司令部的将军们打算在N*M的网格地图上部署他们的炮兵部队。一个N*M的地图由N行M列组成,地图的每一格可能是山地(用“H” 表示),也可能是平原(用“P”表示),如下图。在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队);一支炮兵部队在地图上的攻击范围如图中黑色区域所示:

    如果在地图中的灰色所标识的平原上部署一支炮兵部队,则图中的黑色的网格表示它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格。图上其它白色网格均攻击不到。从图上可见炮兵的攻击范围不受地形的影响。 现在,将军们规划如何部署炮兵部队,在防止误伤的前提下(保证任何两支炮兵部队之间不能互相攻击,即任何一支炮兵部队都不在其他支炮兵部队的攻击范围内),在整个地图区域内最多能够摆放多少我军的炮兵部队。

    n <= 100,m <= 10;

    题解:

    观察到如此小的m,我们首先就要考虑状压,

    但是每一行将会受到2行的影响,因此我们考虑压两行,

    f[i][j][k]表示第i行状态为j,第i-1行状态为k的最优解,

    因此我们有转移方程:

    f[i][j][k]=max(f[i][j][k],f[i-1][k][l] + num[j].one);

    num[j].one 表示状态j下有几个炮兵,

    合法条件如下:

    if(s[i] & num[j].date) continue; 

    if(s[i-1] & num[k].date) continue;

    if(num[j].date & num[k].date) continue;

    if(s[i-2] & num[l].date) continue;

    if((num[j].date & num[l].date) | (num[k].date & num[l].date)) continue;

    其中s[i]表示i行的限制条件,如果第t位不能放炮兵,那么这位就是1,

    j , j ,l 分别为i,i-1,i-2所枚举的状态,

    num[i].date表示当前状态,

    因为要满足不能放炮兵的地方不放炮兵,所以s[i] & num[j].date必须为0,其他行同理,

    这时你肯定注意到了,,,这是个4层循环啊!那复杂度岂不是100 * 1024 *1024 *1024?

    这样的复杂度当然是不行的,观察到每个炮兵的管辖区域相对于仅仅只有10的m来说,其实是非常广的,不管怎么放,最多也就放4个炮兵而已,

    因此我们可以实现找出所有初步合法状态(即满足同一行中炮兵不互相贡献的状态),一共只有59个,,,,

    因此这时的复杂度就可以承受了,

    而且找合法状态的时候还可以顺便就预处理出对应的炮兵个数,是不是非常方便啊~~~~~~~

      1 #include<bits/stdc++.h>
      2 using namespace std;
      3 #define R register int
      4 #define AC 110
      5 int n,m,tot,ans;
      6 int f[AC][65][65];
      7 int s[AC];
      8 char ss[AC];
      9 struct node{
     10     int date,one;
     11 }num[AC];
     12 /*观察到因为m只有10,而一个炮兵的控制区域就有5了,所以一行最多放两个
     13 可行状态很少,m=10都只有59个,所以完全可以打表放进来啊!
     14 f[i][j][k]表示i行状态j,上一行为k*/
     15 inline bool cmp(node a,node b)
     16 {
     17     return a.date < b.date;
     18 }
     19 
     20 void pre()
     21 {
     22     scanf("%d%d",&n,&m);
     23     for(R i=1;i<=n;i++) 
     24     {
     25         scanf("%s",ss+1);
     26         for(R j=1;j<=m;j++) 
     27             if(ss[j] == 'H') s[i] |= (1 << (m - j));//获取这个的状态,1表示不合法 
     28     }
     29 }
     30 
     31 void work1()
     32 {
     33     ++tot;
     34     for(R i=1;i<=m;i++)
     35     {
     36         num[++tot].date=(1 << (i - 1));
     37         num[tot].one=1;
     38     }
     39     for(R i=1;i<=m;i++)
     40         for(R j=i+3;j<=m;j++)
     41         {
     42             num[++tot].date=(1 << (i - 1)) + (1 << (j - 1));
     43             num[tot].one=2;
     44         }
     45     for(R i=1;i<=m;i++)
     46         for(R j=i+3;j<=m;j++)
     47             for(R k=j+3;k<=m;k++)
     48             {
     49                 num[++tot].date=(1 << (i - 1)) + (1 << (j - 1)) + (1 << (k - 1));
     50                 num[tot].one=3;
     51             }
     52     for(R i=1;i<=m;i++)
     53         for(R j=i+3;j<=m;j++)
     54             for(R k=j+3;k<=m;k++)
     55                 for(R l=k+3;l<=m;l++)
     56                 {
     57                     num[++tot].date=(1 << (i - 1)) + (1 << (j - 1)) + (1 << (k - 1)) + (1 << (l - 1));
     58                     num[tot].one=4;
     59                 }
     60 //    for(R i=1;i<=tot;i++)
     61     //    printf("%d have %d
    ",num[i].date,num[i].one);
     62 }
     63 
     64 void work()
     65 {
     66     memset(f,128,sizeof(f));
     67     for(R i=1;i<=tot;i++) 
     68     {
     69         if(s[1] & num[i].date) continue;
     70         f[1][i][0]=num[i].one;//获取第一行
     71     }
     72     for(R i=1;i<=tot;i++) //枚举第二行的状态
     73     {
     74         if(s[2] & num[i].date) continue;
     75         for(R j=1;j<=tot;j++)
     76         {
     77             if(s[1] & num[j].date) continue;
     78             if(num[i].date & num[j].date) continue;
     79             f[2][i][j]=f[1][j][0] + num[i].one;
     80         }
     81     }                                      
     82     for(R i=3;i<=n;i++)//枚举到了哪一行
     83     {
     84         for(R j=1;j<=tot;j++)//枚举当前行状态
     85         {
     86             if(s[i] & num[j].date) continue;//山地不能放
     87             for(R k=1;k<=tot;k++)//枚举上一行状态
     88             {
     89                 if(s[i-1] & num[k].date) continue;
     90                 if(num[j].date & num[k].date) continue;//不能在同一个位置有炮
     91                 for(R l=1;l<=tot;l++)//枚举上上行状态
     92                 {
     93                     if(s[i-2] & num[l].date) continue;
     94                     if((num[j].date & num[l].date) | (num[k].date & num[l].date)) continue;//都不能相互冲突
     95                     f[i][j][k]=max(f[i][j][k],f[i-1][k][l] + num[j].one);        
     96                 }            
     97             }
     98         }
     99     }
    100     for(R i=1;i<=tot;i++)//枚举最后一行的状态
    101         for(R j=1;j<=tot;j++)
    102         {
    103             if((s[n] & num[i].date) || (s[n-1] & num[j].date)) continue;
    104             ans=max(ans,f[n][i][j]);//是否冲突,,,就懒得判了吧,反正也是0
    105         }
    106     printf("%d
    ",ans);
    107 }
    108 
    109 int main()
    110 {
    111 //    freopen("in.in","r",stdin);
    112     pre();
    113     work1();//先找到所有有效情况
    114     work();
    115 //    fclose(stdin);
    116     return 0;
    117 }
  • 相关阅读:
    dlo,学习清单
    OO第一单元优化博客
    BUAA Summer Practice 2017 #1 字符串专场
    OO第一次博客作业
    2018.12.16程设串讲
    助教工作总结 3-22
    软件工程助教3.15总结
    大数据应用技术课程实践--选题与实践方案
    第十五次作业-手写数字识别-小数据集
    第十四次作业-深度学习-卷积
  • 原文地址:https://www.cnblogs.com/ww3113306/p/9097411.html
Copyright © 2011-2022 走看看