LUOGU P1053 篝火晚会 (Noip 2015 )

题目描述

佳佳刚进高中,在军训的时候,由于佳佳吃苦耐劳,很快得到了教官的赏识,成为了“小教官”。在军训结束的那天晚上,佳佳被命令组织同学们进行篝火晚会。一共有 nnn 个同学,编号从 111 到 nnn 。一开始,同学们按照 1,2,…,n1,2,…,n1,2,…,n 的顺序坐成一圈,而实际上每个人都有两个最希望相邻的同学。如何下命令调整同学的次序,形成新的一个圈,使之符合同学们的意愿,成为摆在佳佳面前的一大难题。

佳佳可向同学们下达命令,每一个命令的形式如下:

(b1,b2,…bm−1,bm)(b_1, b_2,… b_{m-1}, b_m)(b1​,b2​,…bm−1​,bm​)

这里 mmm 的值是由佳佳决定的,每次命令 mmm 的值都可以不同。这个命令的作用是移动编号是 b1,b2,…,bmb_1,b_2,…, b_mb1​,b2​,…,bm​ 的这m个同学的位置。要求 b1b_1b1​ 换到 b2b_2b2​ 的位置上, b2b_2b2​ 换到 b3b_3b3​ 的位置上,……,要求 bmb_mbm​ 换到 b1b_1b1​ 的位置上。执行每个命令都需要一些代价。我们假定如果一个命令要移动 mmm 个人的位置,那么这个命令的代价就是 mmm 。我们需要佳佳用最少的总代价实现同学们的意愿,你能帮助佳佳吗?
输入输出格式
输入格式:

第一行是一个整数 n(3≤n≤50000)n(3 le n le 50000)n(3≤n≤50000) ,表示一共有 nnn 个同学。

其后 nnn 行每行包括 222 个不同的正整数,以一个空格隔开,分别表示编号是 111 的同学最希望相邻的两个同学的编号,编号是 222 的同学最希望相邻的两个同学的编号,……,编号是 nnn 的同学最希望相邻的两个同学的编号。

输出格式:

一个整数,为最小的总代价。如果无论怎么调整都不能符合每个同学的愿望,则输出 −1-1−1 。

输入输出样例
输入样例#1:

4
3 4
4 3
1 2
1 2

输出样例#1:

2

说明

对于30%的数据, n≤1000
对于全部的数据, n≤50000

2005提高组第三题

解题思路

首先此题有一个坑点是b是可以不连续的,也就是随便跳就行了。所以对于任意一个不匹配的点,可以让它到它的匹配点,它的匹配点在出来继续往后跳到它的匹配点的匹配点,以此类推就可以使数列匹配,最后的代价就是跳的次数,但这是O(n^2)的,考虑优化。因为它是一个环,所以就可以旋转这个操作,只需要记一个num数组,表示每个匹配点到原来点的距离的个数。然后取这个距离的个数的最大值mx,n-mx即为答案,因为我们可以将环旋转,使这mx个点达到匹配,剩余的点就为n-mx。注意还要反着跑一遍。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;
const int MAXN = 50005;

inline int rd(){
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch))  {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
    return x*f;
}

int n,cnt,num[MAXN];
int a[MAXN][3];
int now[MAXN],ans=1e9;
bool vis[MAXN];

int main(){
    n=rd();
    for(register int i=1;i<=n;i++) a[i][1]=rd(),a[i][2]=rd();
    for(register int i=1;i<=n;i++)
        if((a[a[i][1]][1]!=i && a[a[i][1]][2]!=i )
        ||(a[a[i][2]][1]!=i && a[a[i][2]][2]!=i)){
            puts("-1");
            return 0;
        }
    int x=a[1][1];now[++cnt]=1;vis[1]=1;
    while(x!=1){
        if(vis[x]) break;
        now[++cnt]=x;
        vis[x]=1;
        if(!vis[a[x][1]]) x=a[x][1];
        else x=a[x][2];
    }
    for(register int i=1;i<=n;i++) num[(i-now[i]+n)%n]++;
    for(register int i=0;i<n;i++) ans=min(ans,n-num[i]),num[i]=0;
    cnt=0;now[++cnt]=1;memset(vis,false,sizeof(vis));x=a[1][2];vis[1]=1;
    while(x!=1){
        if(vis[x]) break;
        now[++cnt]=x;
        vis[x]=1;
        if(!vis[a[x][1]]) x=a[x][1];
        else x=a[x][2];
    }
    for(register int i=1;i<=n;i++) num[(i-now[i]+n)%n]++;
    for(register int i=0;i<n;i++) ans=min(ans,n-num[i]);
    printf("%d",ans);
    return 0;
}
原文地址:https://www.cnblogs.com/sdfzsyq/p/9676934.html