篝火晚会

篝火晚会 (并没有算法)

佳佳刚进高中,在军训的时候,由于佳佳吃苦耐劳,很快得到了教官的赏识,成为了“小教官”。在军训结束的那天晚上,佳佳被命令组织同学们进行篝火晚会。一共有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。

​ 首先,我们需要证明对于一个置换,需要交换的次数就是置换中元素不相同的数量。所以O(n^2)的算法就是枚举初始环的一个开始点,然后和目标环比较有多少个元素相同。这样显然会超时。其实我们只要求出对于每一个元素,让初始环的这一个元素和目标环的这一个元素对齐,初始环需要转k圈,接着cnt[k]++。然后O(n)枚举初始环需要转的圈数,答案就是总人数-此置换中已经相同的人数。注意篝火晚会的圈可以对称翻转。

#include <cstdio>
#include <utility>
#include <algorithm>

const int maxn=50005;
int n, side1[maxn], side2[maxn], ans;
int loop[maxn], use[maxn], v1[maxn], v2[maxn];

//做此题前提:一个置换不相同的元素个数等于最小所要交换的元素个数

int main(){
    scanf("%d", &n);
    for (int i=1; i<=n; ++i)
        scanf("%d%d", &side1[i], &side2[i]);
    loop[1]=1; loop[2]=side1[1];
    int t=2, now, pre; bool flag=true;
    while (t<=n){
        now=loop[t]; pre=loop[t-1];
        if (side1[now]!=pre&&side2[now]!=pre){
            flag=false; break; }
        if (side1[now]==pre) loop[++t]=side2[now];
        else loop[++t]=side1[now];
    } //loop就是顺时针的篝火序列
    if (loop[t]!=loop[1]) flag=false;
    if (!flag){
        printf("-1
"); return 0; }
    //枚举这个东西的位置变成loop[i]需要圆顺时针转几圈
    //注意篝火晚会的那个环可以翻转,所以有一个人可以有两个位置
    for (int i=1; i<=n; ++i){
        ++v1[(loop[i]-i+n)%n];
        ++v2[(loop[i]-(n-i+1)+n)%n];
    }
    for (int i=0; i<n; ++i)
        ans=std::max(ans, std::max(v1[i], v2[i]));
    printf("%d
", n-ans);
    return 0;
}
原文地址:https://www.cnblogs.com/MyNameIsPc/p/7722161.html