CF1054E Chips Puzzle

一、题目

点此看题

题目翻译

给你两个 n × m n\times m n×m的矩阵,里面的每一个元素都是 01 01 01串,每次可以把 ( x 1 , y 1 ) (x_1,y_1) (x1,y1)的末尾的字符插入到 ( x 2 , y 2 ) (x_2,y_2) (x2,y2)的首位,要求 ( x 1 , y 1 ) ≠ ( x 2 , y 2 ) (x_1,y_1)\not=(x_2,y_2) (x1,y1)=(x2,y2)并且 x 1 = x 2   o r   y 1 = y 2 x_1=x_2\space or\space y_1=y_2 x1=x2 or y1=y2,求一个合法的操作序列使得给出的第一个矩阵经过操作后成为第二个矩阵。

数据范围

2 ≤ n , m ≤ 300 2\leq n,m\leq300 2n,m300,字符总数不超过 1 e 5 1e5 1e5

二、解法

好像没人发题解,那我来发一个。

一开始看到这道题真的没什么思路,但仔细考虑,本题并未要求操作序列长度最小,所以在一定程度上给了我们乱搞的机会。

考虑一种暴力的构造方法,假设我们能够找到一种状态,让第一个矩阵可以到达,第二个矩阵也可以到达,那我们就可以把它当成中转状态,也就是第一个矩阵的操作序列和第二个矩阵的操作序列反向就可以拼出答案。

我们把所有的 1 1 1放在第一行,所有的 0 0 0放在第二行,这样两个矩阵一定能到达这个状态,且是比较容易操作的。过程中我们要保证(x1,y1),(x2,y2)不同但要有行或列的重合,途中判断一下就行了,看代码吧。

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int N = 305;
const int M = 400005;
int read()
{
    int x=0,flag=1;
    char c;
    while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
    while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
    return x*flag;
}
int n,m,tot,t,cnt[N][N],ans[M][4];string s;
void ins(int x1,int y1,int x2,int y2)
{
    cnt[x2][y2]++;
    ans[++tot][0]=x1;ans[tot][1]=y1;
    ans[tot][2]=x2;ans[tot][3]=y2;
}
void add(int x,int y,int f)
{
    if(f)
    {
        if(x!=1) ins(x,y,1,y);
        else ins(x,y,1,y==1?2:1);
        return ;
    }
    if(x!=2) ins(x,y,2,y);
    else ins(x,y,2,y==1?2:1);
}
int main()
{
    n=read();m=read();
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        {
            cin>>s;
            for(int k=s.size()-1;k>=0;k--)
                add(i,j,s[k]=='1');
        }
    for(int i=1;i<=2;i++)
        for(int j=2;j<=m;j++)
            for(int k=1;k<=cnt[i][j];k++)
                ins(i,j,i,1);
    t=tot;
    //
    memset(cnt,0,sizeof cnt);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        {
            cin>>s;
            for(int k=0;k<s.size();k++)
            //这里要注意,插入顺序要反向
                add(i,j,s[k]=='1');
        }
    for(int i=1;i<=2;i++)
        for(int j=2;j<=m;j++)
            for(int k=1;k<=cnt[i][j];k++)
                ins(i,j,i,1);
    //
    printf("%d\n",tot);
    for(int i=1;i<=t;i++)
        printf("%d %d %d %d\n",ans[i][0],ans[i][1],ans[i][2],ans[i][3]);
    for(int i=tot;i>t;i--)
        printf("%d %d %d %d\n",ans[i][2],ans[i][3],ans[i][0],ans[i][1]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值