[JZOJ5284] 超级翻转

Description

这里写图片描述
这里写图片描述
这里写图片描述

输出任意一种即可,题目有SPJ

Solution

可以考虑先枚举终点。

那么一条路径我们可以通过绕圈来修改它

具体而言,从任意一个点,走到一个格子的左上角,绕着这个格子走一圈,再原路返回,这样相当于只将这个格子四个方向的格子取反

对于起点到枚举的终点的路径,随便怎么走,都可以通过上面绕格子的方式修改成所有的路径

那么枚举终点随便走了一条路径后,原问题转化成可以选择一些格子,将这个格子四周取反,求使所有格子变成白色的方案。

显然操作顺序没有影响,考虑按1~N行做

发现当前行的操作状态完全由上一行决定,因为过了这一行再也没有能修改前一行的操作了

那么上一行的状态前移、后移一位异或上当前行就是当前行的状态,同时对下一行也产生影响,下一行也异或上它

那么当做到最后一行全为0时输出,否则当前枚举的终点无解,继续枚举

关键是第一行的状态

可以枚举,但是复杂度比较大

事实上,第一行全部都不用选,因为可以选择其他行的一些格子,最终的状态与选择第一行的格子等价

因为原矩阵是正方形
这里写图片描述
这里写图片描述
选红色格子,对蓝色格子取反

可以发现选所有的绿色格子,和选红色的格子的影响是等价的

那么第一行就可以全不选

有道题叫玩诈欺的小杉,和这题转化后非常相似
话说那是我最早写的几篇Blog之一。。。。
http://blog.csdn.net/hzj1054689699/article/details/50571564

Code

#include <cstdio>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <cmath>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
#define N 16
using namespace std;
int b[N],a[N],n,rd[N];
void walk(int x,int y,int p,int q)
{
    if(x>p) printf("U"),walk(x-1,y,p,q);
    else if(y>q) printf("L"),walk(x,y-1,p,q);
    else if(y<q) printf("R"),walk(x,y+1,p,q);
    else if(x<p) printf("D"),walk(x+1,y,p,q);
}
void fx(int x,int y) 
{
    b[x]^=(1<<(y-1));
}
void chd(int x,int y)
{
    if(y<=n) fx(x,y);
    if(y>1) fx(x,y-1); 
}
void chh(int x,int y)
{
    if(x<=n) fx(x,y);
    if(x>1) fx(x-1,y);
}
void wk(int x,int y,int p,int q)
{
    if(x>p) chd(x-1,y),wk(x-1,y,p,q);
    else if(y>q) chh(x,y-1),wk(x,y-1,p,q);
    else if(y<q) chh(x,y),wk(x,y+1,p,q);
    else if(x<p) chd(x,y),wk(x+1,y,p,q);
}
int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        memset(b,0,sizeof(b));
        memset(a,0,sizeof(a));
        scanf("%d",&n);
        if(n==1) 
        {
            int v,p,q;
            scanf("%d",&v);
            scanf("%d%d",&p,&q);
            if(v==0)printf("\n");
            else
            {
                if(p==1) printf("D\n");
                else printf("U\n"); 
            }
            continue;
        }
        fo(i,1,n)
        {
            fo(j,1,n) scanf("%d",&rd[n-j+1]);
            fo(j,1,n) 
            {
                b[i]=b[i]*2+rd[j];
                a[i]=b[i];
            }
        }
        int x,y,v=(1<<n),bp=0;
        scanf("%d%d",&x,&y);
        fo(p,1,n+1)
        {
            fo(q,1,n+1)
            {
                fo(i,1,n+1) b[i]=a[i];
                wk(x,y,p,q);
                fo(i,2,n)
                {
                    b[i]=b[i]^(b[i-1]*2%v)^(b[i-1]/2);
                    b[i+1]=b[i+1]^b[i-1];
                }
                if(b[n]!=0) continue;
                else
                {
                    bp=1;
                    walk(x,y,p,q);
                    fo(i,2,n)
                    {
                        fo(j,1,n)
                        {
                            if(b[i-1]&((1<<(j-1))))
                            {
                                walk(p,q,i,j);
                                printf("RDLU");
                                walk(i,j,p,q);
                            }
                        }
                    }
                    printf("\n");
                    break;
                }
                if(bp)break;
            }
            if(bp) break;
        }
        if(!bp) printf("No Solution!\n");
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值