SGU 307: Cipher

题目链接:http://acm.sgu.ru/problem.php?contest=0&problem=307


题目大意:

现在有一个网格,每个格子上填了1或0。

先给出每相邻四个格子的和。

求可行方案


算法:

其实我们不难看出,

整个网格的状态是由第一行和第一列决定的。

我们不妨枚举(0,0)的状态,

然后再根据给出的和对于所有其它第一行和第一列的格子做限制。

然后通过2-SAT得出第一行和第一列的合法状态。

最后简单递推即可。


代码如下:

#include<cstdio>
#include<cstring>
#include<stack>
#include<algorithm>
#include<vector>
#include<climits>
using namespace std;
const int MAXN=1500;
const int MAXM=310;
int blkn;
int low[MAXN],dep[MAXN],blk[MAXN],fst[MAXN],hash[MAXN];
int a[MAXM][MAXM],b[MAXM][MAXM],c[MAXM][MAXM];
vector<int>map[MAXN];
stack<int>stk;

void tarjan(int p,int u)
{
  if(dep[u]==-1)
    {
      int tmp=low[u]=dep[u]=(p==-1)?0:dep[p]+1;
      stk.push(u);
      for(int i=0; i<map[u].size(); i++)
        {
          int v=map[u][i];
          tarjan(u,v);
          tmp=min(tmp,low[v]);
        }
      low[u]=tmp;
      if(low[u]==dep[u])
        {
        	fst[blkn]=u;
          while(1)
            {
              int v=stk.top();
              stk.pop();
              blk[v]=blkn;
              low[v]=INT_MAX;
              if(u==v)break;
            }
          blkn++;
        }
    }
}

int main()
{
  int n,m;
  scanf("%d%d",&n,&m);
  char s[500];
  getchar();
  for(int i=1; i<n; i++)
    {
      gets(s);
      for(int j=1; j<m; j++)
        c[i][j]=s[j-1]-'0';
    }
  for(int state=0; state<2; state++)
    {
      memset(dep,-1,sizeof(dep));
      blkn=0;
      for(int i=0; i<2*(n+m-2); i++)
        {
          map[i].clear();
        }
      memset(b,0,sizeof(b));
      a[0][0]=b[0][0]=state;
      for(int i=1; i<n; i++)
        for(int j=1; j<m; j++)
          {
            b[i][j]=c[i][j]-b[i-1][j]-b[i][j-1]-b[i-1][j-1];
          }
      for(int i=1; i<n; i++)
        for(int j=1; j<m; j++)
          for(int x=0; x<2; x++)
            for(int y=0; y<2; y++)
              {
                int tmp=b[i][j];
                if(i&1)
                  tmp-=x;
                else
                  tmp+=x;
                if(j&1)
                  tmp-=y;
                else
                  tmp+=y;
                if(tmp<0||tmp>1)
                  {
                    int lx=((i-1)<<1)+y;
                    int ly=((n+j-2)<<1)+x;
                    map[lx].push_back(ly^1);
                    map[ly].push_back(lx^1);
                  }
              }
      for(int i=0; i<2*(n+m-2); i++)
        {
          tarjan(-1,i);
        }
      {
        int i;
        for(i=0; i<2*(n+m-2); i+=2)
          {
            if(blk[i]==blk[i|1])break;
          }
        if(i<2*(n+m-2))
          {
            continue;
          }
      }
      memset(hash,-1,sizeof(hash));
      for(int i=0; i<blkn; i++)
        if(hash[i]==-1)
          {
            hash[i]=1;
            hash[blk[fst[i]^1]]=0;
          }
      for(int i=1; i<n; i++)
        a[i][0]=hash[blk[((i-1)<<1)|1]];
      for(int j=1; j<m; j++)
        a[0][j]=hash[blk[((n+j-2)<<1)|1]];
      for(int i=1; i<n; i++)
        for(int j=1; j<m; j++)
          {
            a[i][j]=c[i][j]-a[i-1][j-1]-a[i-1][j]-a[i][j-1];
          }
      for(int i=0; i<n; i++)
        {
          if(i)puts("");
          for(int j=0; j<m; j++)
            {
              printf("%d",a[i][j]);
            }
        }
      return 0;
    }
  puts("CORRUPT");
  return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值