洛谷P1053篝火晚會題解--zhengjun

題目描述

佳佳剛進高中,在軍訓的時候,由於佳佳吃苦耐勞,很快得到了教官的賞識,成爲了“小教官”。在軍訓結束的那天晚上,佳佳被命令組織同學們進行篝火晚會。一共有nn個同學,編號從11nn。一開始,同學們按照1,2,,n1,2,…,n的順序坐成一圈,而實際上每個人都有兩個最希望相鄰的同學。如何下命令調整同學的次序,形成新的一個圈,使之符合同學們的意願,成爲擺在佳佳面前的一大難題。

佳佳可向同學們下達命令,每一個命令的形式如下:

(b1,b2,...bm1,bm)(b_1, b_2,... b_{m-1}, b_m)這裏mm的值是由佳佳決定的,每次命令mm的值都可以不同。這個命令的作用是移動編號是b1,b2,,bmb_1,b_2,…, b_m的這mm個同學的位置。要求b1b_1換到b2b_2的位置上,b2b_2換到b3b_3的位置上,……,要求bmb_m換到b1b_1的位置上。執行每個命令都需要一些代價。我們假定如果一個命令要移動mm個人的位置,那麼這個命令的代價就是mm。我們需要佳佳用最少的總代價實現同學們的意願,你能幫助佳佳嗎?

輸入格式

第一行是一個整數n(3n50000)n(3 \le n \le 50000),表示一共有nn個同學。

其後nn行每行包括22個不同的正整數,以一個空格隔開,分別表示編號是11的同學最希望相鄰的兩個同學的編號,編號是22的同學最希望相鄰的兩個同學的編號,……,編號是nn的同學最希望相鄰的兩個同學的編號。

輸出格式

一個整數,爲最小的總代價。如果無論怎麼調整都不能符合每個同學的願望,則輸出1-1

輸入輸出樣例

輸入 #1 複製
4
3 4
4 3
1 2
1 2
輸出 #1 複製
2

說明/提示

對於30%30\%的數據,n1000n \le 1000
對於全部的數據,n50000n \le 50000

20052005提高組第三題

思路

首先,爲了讓總代價最少,一定就要讓移動的人次最少,我們方法就是找到一組儘可能多的不用移動的,而剩下的一坨,有可能是一次換的,有可能是兩次……但是,無論是多少次,最少的代價一定就是總個數,因爲每個人只需要換一次就可以到達他的位置,連成一個環,如果還有其他的環,就再每人換一次,又成一個環,知道換完位置。這樣每個人就只需要移動一次就夠了。

那麼,問題就轉換成了求最多可以有多少個人不用動。

所以,我就打了一個模擬,直接從頭到尾看看有多少不用動,結果,聽取wawa聲一片

額,我再乍一看,一個是初始環,另一個是目標環,要找到最多能有多少一樣的,應該要一次一次對過去,然後,有一個方法,就是求出他要移動多少格纔可以到達最後的目標點。

這樣,移動格數相同的就一定是同一組的,也就是同一次移動的,然後找到最多的一組就可以了。

結果代碼一交,又是wawa

然後又細細一想,啊,目標環還有可能是另一個方向擺的,就是比如說樣例,可能是1 3 2 4,也有可能是1 4 2 3,所以就要兩次。

結果交上去,終於ACAC了(太不容易了

代碼

#include<bits/stdc++.h>
#define max(x,y) ((x)>(y)?(x):(y))
#define maxn 50001
using namespace std;
int n;
int a[maxn],b[maxn];//每個點的兩邊的點
bool flag[maxn];//判斷是否已經用過了
int k[maxn],f[maxn];//目標序列,距離目標點有多少距離
int sum[maxn];//桶計數
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d%d",&a[i],&b[i]);
	int now=1;
	k[1]=1;
	flag[now]=1;
	for(int i=2;i<=n;i++){
		if(!flag[a[now]]){//左邊的點還沒有用過
			k[i]=a[now];
			now=a[now];
			flag[now]=1;
		}
		else if(!flag[b[now]]){//右邊的點還沒用過
			k[i]=b[now];
			now=b[now];
			flag[now]=1;
		}
		else{//兩邊的點都用過了,判無解
			printf("-1");
			return 0;
		}
	}
	for(int i=1;i<=n;i++)f[i]=(i-k[i]+n)%n;//求距離
	int maxx=0;
	for(int i=1;i<=n;i++){
		sum[f[i]]++;//桶計數
		maxx=max(maxx,sum[f[i]]);//求最大值
	}
	//重來一遍
	memset(sum,0,sizeof(sum));//清空
	memset(flag,0,sizeof(flag));//清空
	now=1;
	k[1]=1;
	flag[now]=1;
	for(int i=2;i<=n;i++){
		if(!flag[b[now]]){//反過來就可以求另一種順序了
			k[i]=b[now];
			now=b[now];
			flag[now]=1;
		}
		else if(!flag[a[now]]){
			k[i]=a[now];
			now=a[now];
			flag[now]=1;
		}
	}
	for(int i=1;i<=n;i++)f[i]=(i-k[i]+n)%n;
	for(int i=1;i<=n;i++){
		sum[f[i]]++;
		maxx=max(maxx,sum[f[i]]);
	}
	printf("%d",n-maxx);//注意最後要用來減
	return 0;
}

謝謝–zhengjun

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章