[ GDOI 2014 ] 拯救莫莉斯

(\)

(Description)


有一个 (N imes M) 的网格,每个格点都有权值,图是四连通的。

现在选择一个点集,使得每个格点要么被选中,要么连通的点之一被选中。

求这个点集的权值和最小值,在保证前两条满足的前提下,点集大小最小是多少。

  • (N imes Mle 50, Mle N)

(\)

(Solution)


状压入门题目。

注意到在题面的限制条件下,(M) 最大是(7),我们不妨状压每一行的情况。

注意一个方案里一行合法的要求,那就是上、下、本行和本行左右移的方案或起来得到的二进制位是满的。

那么我们状态里就不能只记录当前行了,因为无法确定是否合法。

(f[i][S][S_1]) 表示第 (i) 行放置方案为 (S) ,第 (i-1) 行放置方案为 (S_1)(i-1) 行都合法的前提下 的最优解。

那么注意到一行的确定是由三行确定的,所以我们需要枚举行数 (i) ,第 (i-2) 行的放置情况 (S_2) ,第 (i-1) 行的放置情况 (S_1) ,第 (i) 行的放置情况 (S) ,转移条件自然是

[S_2|S_1|(S_1<<1)|(S_1>>1)|S ge (1<<M)-1 ]

然后状态维护一个结构体,手写一个结构体取 (min) 就好了。

注意初始化,有答案的应该只有 (f[1][k][0]),其他都要置成 (inf)

注意答案的选取,合法的答案需要满足 (S_1|S|(S<<1)|(S>>1) ge (1<<M)-1)

同行左右移的辅助一开始忘记了,状态压缩类型的题目还是要注意考虑全面。

(\)

(Code)


#include<cmath>
#include<cstdio>
#include<cctype>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 51
#define R register
#define gc getchar
#define inf 2000000000
using namespace std;

inline int rd(){
  int x=0; bool f=0; char c=gc();
  while(!isdigit(c)){if(c=='-')f=1;c=gc();}
  while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();}
  return f?-x:x;
}

int n,m,lim,mp[N][10],cost[N][1<<8],cnt[1<<8];

struct dp{int w,cnt;}f[N][1<<8][1<<8],ans;

inline dp mn(dp x,dp y){
  if(x.w!=y.w) return (x.w<y.w)?x:y;
  return (x.cnt<y.cnt)?x:y;
}

int main(){
  n=rd(); m=rd(); lim=(1<<m)-1;
  for(R int i=1;i<=n;++i)
    for(R int j=0;j<m;++j) mp[i][j]=rd();
  for(R int i=1;i<=n;++i)
    for(R int j=0;j<=lim;++j)
      for(R int k=0;k<m;++k) if(j&(1<<k)) cost[i][j]+=mp[i][k];
  for(R int i=0;i<=lim;++i)
    for(R int j=0;j<m;++j) if(i&(1<<j)) ++cnt[i];
  for(R int i=0;i<=n;++i)
    for(R int s=0;s<=lim;++s)
      for(R int s1=0;s1<=lim;++s1)
        f[i][s][s1].w=f[i][s][s1].cnt=inf;
  for(R int i=0;i<=lim;++i){
    f[1][i][0].w=cost[1][i];
    f[1][i][0].cnt=cnt[i];
  }
  for(R int i=2;i<=n;++i)
    for(R int s=0;s<=lim;++s)
      for(R int s1=0;s1<=lim;++s1)
        for(R int s2=0;s2<=lim;++s2)
          if(((s|s1|s2|(s1<<1)|(s1>>1))&lim)==lim)
            f[i][s][s1]=mn(f[i][s][s1],(dp){f[i-1][s1][s2].w+cost[i][s],f[i-1][s1][s2].cnt+cnt[s]});
  ans.w=ans.cnt=inf;
  for(R int s1=0;s1<=lim;++s1)
    for(R int s2=0;s2<=lim;++s2)
      if(((s1|s2|(s1<<1)|(s1>>1))&lim)==lim) ans=mn(ans,f[n][s1][s2]);
  printf("%d %d",ans.cnt,ans.w);
  return 0;
}


原文地址:https://www.cnblogs.com/SGCollin/p/9816293.html