Vijos P1008 篝火晚会

11 篇文章 0 订阅

Vijos P1008 篝火晚会


描述

佳佳刚进高中,在军训的时候,由于佳佳吃苦耐劳,很快得到了教官的赏识,成为了“小教官”。在军训结束的那天晚上,佳佳被命令组织同学们进行篝火晚会。一共有n个同学,编号从1到n。一开始,同学们按照1,2,……,n的顺序坐成一圈,而实际上每个人都有两个最希望相邻的同学。如何下命令调整同学的次序,形成新的一个圈,使之符合同学们的意愿,成为摆在佳佳面前的一大难题。
佳佳可向同学们下达命令,每一个命令的形式如下:
(b1, b2,… bm -1, bm)
这里m的值是由佳佳决定的,每次命令m的值都可以不同。这个命令的作用是移动编号是b1,b2,…… bm –1,bm的这m个同学的位置。要求b1换到b2的位置上,b2换到b3的位置上,……,要求bm换到b1的位置上。
执行每个命令都需要一些代价。我们假定如果一个命令要移动m个人的位置,那么这个命令的代价就是m。我们需要佳佳用最少的总代价实现同学们的意愿,你能帮助佳佳吗?
对于30%的数据,n <= 1000;
对于全部的数据,n <= 50000。
格式

输入格式

输入的第一行是一个整数n(3 <= n <= 50000),表示一共有n个同学。其后n行每行包括两个不同的正整数,以一个空格隔开,分别表示编号是1的同学最希望相邻的两个同学的编号,编号是2的同学最希望相邻的两个同学的编号,……,编号是n的同学最希望相邻的两个同学的编号。
输出格式

输出包括一行,这一行只包含一个整数,为最小的总代价。如果无论怎么调整都不能符合每个同学的愿望,则输出-1。
样例1

样例输入1

4
3 4
4 3
1 2
1 2

样例输出1

2

限制

1s


题解

考虑到如果该数据有解,那么一定会以节点 1 1 先向左/右走一步,那么这整个环就确定下来了,但是它会有两个(镜像环)

然后就是用一个贪心的方法,从环上任意一个节点开始走,求出走到该点所要的时间,以及改点的id他们的差值,记录这个差值

显然,某个差值出现次数越多,说明以这些差值出现的节点所构成的环所需要调整的人数越少,答案就是 n n -出现次数的最大值

然后,就是判断是否存在解

首先,对于一个节点,它被需要的次数超过2次 ,那么一定不存在解,因为是一个环,一个人的临近位置只有两个

其次,对于一个环,如果他不能包括所有的点,那么它也一定不存在解(数据的第二个点,我开始只 A A <script type="math/tex" id="MathJax-Element-707">A</script>了第二个点,所以知道)

最后,对于一个点,如果它上一个点的下一个点不是它自己,那么它也没有解(好像这个只适用于起点,反正我也写上去了)


代码

#include<cstdio>
#include<cstring>
#define maxn 50005
using namespace std;

int n,tot,ans,last;
int p[maxn],cnt[maxn],l[maxn],r[maxn],hash[maxn];
bool vis[maxn];

int readln()
{
    int x=0;
    char ch=getchar();
    while (ch<'0'||ch>'9') ch=getchar();
    while ('0'<=ch&&ch<='9') x=x*10+ch-48,ch=getchar();
    return x;
}

int max(int x,int y){return x>y?x:y;}

int main()
{
    n=readln();
    for (int i=1;i<=n;i++)
    {
        l[i]=readln();r[i]=readln();
        cnt[l[i]]++;cnt[r[i]]++;
        if (cnt[l[i]]>2||cnt[r[i]]>2) {printf("-1");return 0;}
    }
    last=l[1];
    for (int i=1;!vis[i];i=(vis[r[i]]?l[i]:r[i]))
    {
        vis[i]=true;
        if (l[last]!=i&&r[last]!=i) {printf("-1");return 0;}
        p[++tot]=i;hash[(i-tot+n)%n]++;last=i;
    }
    if (tot<n) {printf("-1");return 0;}
    for (int i=0;i<n;i++) ans=max(ans,hash[i]);
    memset(vis,false,sizeof(vis));
    memset(hash,0,sizeof(hash));
    tot=0;
    for (int i=1;!vis[i];i=(vis[l[i]]?r[i]:l[i]))
    {
        vis[i]=true;
        p[++tot]=i;hash[(i-tot+n)%n]++;
    }
    for (int i=0;i<n;i++) ans=max(ans,hash[i]);
    printf("%d",n-ans);
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值