NOIP2008普及組 排座椅(重慶一中高2018級信息學競賽測驗5) 解題報告

【問題描述】  
  
  上課的時候總有一些同學和前後左右的人交頭接耳,這是令小學班主任十分頭疼的一件事情。不過,班主任小雪發現了一些有趣的現象,當同學們的座次確定下來之後,只有有限的D對同學上課時會交頭接耳。同學們在教室中坐成了M行N列,坐在第i行第j列的同學的位置是(i,j),爲了方便同學們進出,在教室中設置了K條橫向的通道,L條縱向的通道。於是,聰明的小雪想到了一個辦法,或許可以減少上課時學生交頭接耳的問題:她打算重新擺放桌椅,改變同學們桌椅間通道的位置,因爲如果一條通道隔開了兩個會交頭接耳的同學,那麼他們就不會交頭接耳了。


  請你幫忙給小雪編寫一個程序,給出最好的通道劃分方案。在該方案下,上課時交頭接耳的學生對數最少。 
 
    
 【輸入格式】  
  
  第一行,有5各用空格隔開的整數,分別是M,N,K,L,D(2<=N,M<=1000,0<=K<M,0<=L<N,D<=2000)。
  下來D行,每行有4個用空格隔開的整數,第i行的4個整數Xi,Yi,Pi,Qi,表示坐在位置(Xi,Yi)與(Pi,Qi)的兩個同學會交頭接耳(輸入保證他們前後相鄰或者左右相鄰)。
  輸入數據保證最優方案的唯一性。


 
    
 【輸出格式】  
   
  第一行包含K個整數,a[1] a[2] … a[K],表示第a[1]行和a[1+1]行之間、第a[2]行和第a[2+1]行之間、…、第a[K]行和第a[K+1]行之間要開闢通道,其中a[i]< a[i+1],每兩個整數之間用空格隔開(行尾沒有空格)。
  第二行包含L個整數,b[1] b[2] … b[k],表示第b[1]列和b[1+1]列之間、第b[2]列和第b[2+1]列之間、…、第b[L]列和第b[L+1]列之間要開闢通道,其中b[i]< b[i+1],每兩個整數之間用空格隔開(行尾沒有空格)。


 
    
 【輸入樣例】   
   
4 5 1 2 3
4 2 4 3
2 3 3 3
2 5 2 4


 
    
 【輸出樣例】  
   
2
2 4
 
    
 【樣例解釋】  
   
      
  上圖中用符號*、※、+ 標出了3對會交頭接耳的學生的位置,圖中3條粗線的位置表示通道,圖示的通道劃分方案是唯一的最佳方案。 


做題思路(正解):本題的主要算法爲貪心,要使上課時交頭接耳的學生對數最少,則應劃分交頭接耳的學生對數最多的通道,因此我們可以定義一個結構體來記錄每條通道的交頭接耳的學生對數(分別存儲,該條通道的編號和交頭接耳的學生對數)。在找要劃分的通道時,可以先將通道按交頭接耳的學生對數由大到小排序,然後再進行選擇,將選擇的通道存入數組中(橫向和縱向分開存),最後輸出答案。需要注意的是,輸出需劃分的通道時,要先將通道按編號由小到大排序後再輸出(考試時我忘了這一點,結果只對了一組數據)。


#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn=1005;
int M,N,K,L,D;
struct data  //記錄交頭接耳的學生
{
	int x,y,p,q;
};
data a[2*maxn];
struct data2  //記錄通道
{
	int x,y,num;
};
data2 b[2*maxn];
int X[maxn],Y[maxn];  //記錄答案
bool cmp(data2 aa,data2 bb)
{
	return aa.num>bb.num;
}
int main()
{
	freopen("seat.in","r",stdin);
	//freopen("seat.out","w",stdout);
	scanf("%d%d%d%d%d",&M,&N,&K,&L,&D);
	for(int i=1;i<=D;i++)
	scanf("%d%d%d%d",&a[i].x,&a[i].y,&a[i].p,&a[i].q);
	memset(b,0,sizeof(b));
	for(int i=1;i<=M;i++)  //先將通道的編號存進結構體中
	b[i].x=i;
	for(int i=1;i<=N;i++)
	b[i+M].y=i;
	for(int i=1;i<=D;i++)  //計算每條通道交頭接耳的學生對數
	{
		int xx=min(a[i].x,a[i].p),yy=min(a[i].y,a[i].q);  
		if(a[i].x!=a[i].p)  b[xx].num++;
		if(a[i].y!=a[i].q)  b[yy+M].num++;
	}
	sort(b+1,b+1+M+N,cmp);  //將通道按交頭接耳學生對數由大到小排序
	int cnt1=0,cnt2=0;
	for(int i=1;i<=M+N;i++)  //選擇要劃分的通道
	{
		if(cnt1<K && b[i].x>0)  X[++cnt1]=b[i].x;
		if(cnt2<L && b[i].y>0)  Y[++cnt2]=b[i].y;
		if(cnt1==K && cnt2==L)  break;
	}
	sort(X+1,X+1+K);  //輸出答案時要由小到大輸出
	sort(Y+1,Y+1+L);
	for(int i=1;i<=K;i++)
	{
		if(i!=K)  printf("%d ",X[i]);
		else  printf("%d",X[i]);
	}
	printf("\n");
	for(int i=1;i<=L;i++)
	{
		if(i!=L)  printf("%d ",Y[i]);
		else  printf("%d",Y[i]);
	}
	printf("\n");
	return 0;
}
考後反思:細節決定成敗,考試時就是因爲沒有注意輸出,而失分,之後在做題時一定要回過頭來檢查輸入和輸出。
發佈了57 篇原創文章 · 獲贊 8 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章