NOIP 2008 排座椅(贪心)

题目描述

上课的时候总有一些同学和前后左右的人交头接耳,这是令小学班主任十分头疼的一件事情。不过,班主任小雪发现了一些有趣的现象,当同学们的座次确定下来之后,只有有限的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个整数,a1 a2...aK,表示第a1行和a1+1行之间、第a2行和第a2+1行之间、...、第aK行和第aK+1行之间要开辟通道,其中ai<ai+1,每两个整数之间用空格隔开(行尾没有空格)。 
第二行包含L个整数,b1 b2...bk,表示第b1列和b1+1列之间、第b2列和第b2+1列之间、...、第bL列和第bL+1列之间要开辟通道,其中bi<bi+1,每两个整数之间用空格隔开(行尾没有空格)。 


下面是对样例数据的解释: 

 

上图中用符号*、※、+标出了3对会交头接耳的学生的位置,图中3条粗线的位置表示通道,图示的通道划分方案是唯一的最佳方案。 

 

分析:要想让交头接耳的学生对数最少,那么通道就要尽可能的隔开更多的学生,而输入数据又是保证他们前后相邻或者左右相邻的,因此,用两个数组,一个记录行,一个记录列,来记录各行各列的人数,然后从大到小依次找出K个行通道,也就是找K个行上人数多的,再以同样的方式找出L个列通道,也就是找L个列上人数多的,对于每次找的行通道和列通道,需要用两个数组来标记哪些行列位置被设置了通道,然后输出被标记的通道即可,具体细节看代码注释。

#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<stack>
#include<cstdio>
#include<vector>
#include<cctype>
#include<cstring>
#include<utility>
#include<cstdlib>
#include<iomanip>
#include<iostream>
#include<algorithm>
#define Clear(x) memset(x,0,sizeof(x))
#define fup(i,a,b) for(int i=a;i<b;i++)
#define rfup(i,a,b) for(int i=a;i<=b;i++)
#define fdn(i,a,b) for(int i=a;i>b;i--)
#define rfdn(i,a,b) for(int i=a;i>=b;i--)
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
const int maxn=1e3+7;
int M,N,K,L,D;
int h[maxn],l[maxn];//行,列
int ph[maxn],pl[maxn];//需要打印(print)的行,列

int read()
{
    char ch=getchar();int ret=0,f=1;
    while(ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){ret=ret*10+ch-'0';ch=getchar();}
    return f*ret;
}

void input()
{
    Clear(h),Clear(l),Clear(ph),Clear(pl);
    M=read(),N=read(),K=read(),L=read(),D=read();
    int x1,y1,x2,y2;
    fup(i,0,D)
    {
        x1=read(),y1=read(),x2=read(),y2=read();
        if(x1==x2) l[min(y1,y2)]++;//因为a1通道表示的是a1列和a1+1列之间有通道,题上没有保证第二个一定比第一个的大,所以取较小的一个
        else h[min(x1,x2)]++;//和上面同理
    }
}

void print()
{
    int flag=1;
    fup(i,0,M)
    {
        if(ph[i])
        {
            if(flag) flag=0;
            else printf(" ");
            printf("%d",i);
        }
    }
    printf("\n");
    flag=1;
    fup(i,0,N)
    {
        if(pl[i])
        {
            if(flag) flag=0;
            else printf(" ");
            printf("%d",i);
        }
    }
    printf("\n");
}

void slove()
{
    fup(i,0,K)//设置行的通道
    {
        int max_pos=-1;//人数最多的一行的行标
        int max_value=-1;//人数最多的这一行的人数
        fup(j,1,M)
        {
            if(h[j]>max_value)
            {
                max_value=h[j];
                max_pos=j;
            }
        }
        ph[max_pos]=1;//标记这一行设置了通道
        h[max_pos]=0;//这一行已经设置为通道
    }
    fup(i,0,L)//设置列的通道
    {
        int max_pos=-1;
        int max_value=-1;
        fup(j,1,N)
        {
            if(l[j]>max_value)
            {
                max_value=l[j];
                max_pos=j;
            }
        }
        pl[max_pos]=1;//标记这一列设置了通道
        l[max_pos]=0;//这一列已被设置为通道
    }
    print();
}

int main()
{
    input();
    slove();
    return 0;
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值