NOIP2008普及组 排座椅(重庆一中高2018级信息学竞赛测验5) 解题报告

【问题描述】  
  
  上课的时候总有一些同学和前后左右的人交头接耳,这是令小学班主任十分头疼的一件事情。不过,班主任小雪发现了一些有趣的现象,当同学们的座次确定下来之后,只有有限的D对同学上课时会交头接耳。同学们在教室中坐成了M行N列,坐在第i行第j列的同学的位置是(i,j),为了方便同学们进出,在教室中设置了K条横向的通道,L条纵向的通道。于是,聪明的小雪想到了一个办法,或许可以减少上课时学生交头接耳的问题:她打算重新摆放桌椅,改变同学们桌椅间通道的位置,因为如果一条通道隔开了两个会交头接耳的同学,那么他们就不会交头接耳了。


  请你帮忙给小雪编写一个程序,给出最好的通道划分方案。在该方案下,上课时交头接耳的学生对数最少。 
 
    
 【输入格式】  
  
  第一行,有5各用空格隔开的整数,分别是M,N,K,L,D(2<=N,M<=1000,0<=K   下来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
 
    
 【样例解释】  
   
    NOIP2008普及组 排座椅(重庆一中高2018级信息学竞赛测验5) 解题报告_第1张图片   
  上图中用符号*、※、+ 标出了3对会交头接耳的学生的位置,图中3条粗线的位置表示通道,图示的通道划分方案是唯一的最佳方案。 


做题思路(正解):本题的主要算法为贪心,要使上课时交头接耳的学生对数最少,则应划分交头接耳的学生对数最多的通道,因此我们可以定义一个结构体来记录每条通道的交头接耳的学生对数(分别存储,该条通道的编号和交头接耳的学生对数)。在找要划分的通道时,可以先将通道按交头接耳的学生对数由大到小排序,然后再进行选择,将选择的通道存入数组中(横向和纵向分开存),最后输出答案。需要注意的是,输出需划分的通道时,要先将通道按编号由小到大排序后再输出(考试时我忘了这一点,结果只对了一组数据)。


#include
#include
#include
#include
#include
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(cnt10)  X[++cnt1]=b[i].x;
		if(cnt20)  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;
}
考后反思:细节决定成败,考试时就是因为没有注意输出,而失分,之后在做题时一定要回过头来检查输入和输出。

你可能感兴趣的:(竞赛测验,贪心算法)