#ACW 4084 号码牌(无向图连通性+简单拓扑序)

1.题意

链接: 原题链接.

给你一个1~n全排列A,让你判断能否通过一种操作使之变为目标排列B。该操作是:交换与当前下标i相距 d i d_i di的2个元素。
如:下标为5, d 5 = 2 d_5=2 d5=2,那么可以将下标为5处的元素和下标为3或7处的元素交换。

2.思路

从图的角度思考,假如下标为a处可以到达下标为b处,那么就从a向b连一条有向边,我们发现交换是相互的,故可以把这条有向边改成无向边。
我们进一步发现想让下标为i的数变为 a i a_i ai,而 a i a_i ai在下标j,那么只要从i到j有路径就可以了。
那么是否只要对于每个下标i,其与目标排列的 a i a_i ai所在下标j间连通,就一定存在一种合理交换方案得到目标排列?
答案是肯定的,构造方法类似于拓扑排序,在每个连通块中,先找到度为1的点,把对应元素移动过去,然后去掉唯一的一条边,循环进行,可得出一定有解。(一个连通块,按照topo序删点,剩下的模块依旧是一个连通块)
由于本题只需要判断真假,故只需要判断目标下标和原下标的连通性即可,这可以使用并查集或者floodfill算法,如下代码用的是后者。(啊,,比赛的时候并查集都忘了该咋写了~)

3.代码

#include <iostream>

using namespace std;

const int N=110;

bool g[N][N];
int n;
int a[N],d[N],con[N];
bool st[N];
int cnt;

void dfs(int u)
{
    st[u]=true;
    con[u]=cnt;
    for(int i=0;i<n;i++)
    {
        if(g[u][i]&&!st[i])
        {
            dfs(i);
        }
    }
    return;
}

void floodfill()
{
    for(int i=0;i<n;i++)
    {
        if(!st[i])
        {
            cnt++;
            dfs(i);
        }
    }
}

bool check()
{
    for(int i=0;i<n;i++)if(con[a[i]-1]!=con[i])return false;
    return true;
}

int main()
{
    cin>>n;
    for(int i=0;i<n;i++)cin>>a[i];
    for(int i=0;i<n;i++)cin>>d[i];
    for(int i=0;i<n;i++)
    {
        if(d[i]+i<n)
        {
            g[i][i+d[i]]=1;
            g[i+d[i]][i]=1;
        }
        if(i-d[i]>=0)
        {
            g[i][i-d[i]]=1;
            g[i-d[i]][i]=1;
        }
    }
    floodfill();
    if(check())cout<<"YES\n";
    else cout<<"NO\n";
    
}

4.收获

  1. 当遇到比较复杂的题目,考虑用图论的角度思考问题
  2. 拓扑序的优秀性质——一个连通块,按照topo序删点,剩下的模块依旧是一个连通块。诶呦,不错哟~
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值