UVA11134 Fabled Rooks

 
题意:在一个n*n(1<=n<=5000)的棋盘上放置n个车,每个车都只能在给定的一个矩形里放置,使其n个车两两不在同一行和同一列,判断是否有解,若有解,给出任意可行方案。
 
这道题首先要看出,矩形实际上就是横坐标和纵坐标的限制,但是横坐标和纵坐标的限制是无关的,我们可以先把每个点的横坐标求出来再把每个点的纵坐标求出来。
那么就变成有关区间的一个问题:给出n个区间,每个区间是否存在一个点,使n个点不相同。
我看到这道题,感觉似乎不是很难,然后用10分钟打了一个贪心,交上去WA了。
WA的代码如下:
//Serene
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
using namespace std;
const int maxn=5000+10;
int n,ans[2][maxn];

int aa;char cc;
int read() {
	aa=0;cc=getchar();
	while(cc<'0'||cc>'9') cc=getchar();
	while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();
	return aa;
}

struct Node{
	int l,r,pos;
}node[2][maxn];

bool cmp(const Node& a,const Node& b) {
	return a.l==b.l? a.r<b.r:a.l<b.l;
}

void D() {
	for(int p=0;p<2;++p)
		for(int i=1;i<=n;++i) {
			if(node[p][i].l>i||node[p][i].r<i) {
				printf("IMPOSSIBLE\n");
				return;
			}
			ans[p][node[p][i].pos]=i;
		}
	for(int i=1;i<=n;++i) printf("%d %d\n",ans[0][i],ans[1][i]);
}

int main() {
	n=read();
	while(n) {
		for(int i=1;i<=n;++i) {
			node[0][i].l=read(); node[1][i].l=read();
			node[0][i].r=read(); node[1][i].r=read();
			node[0][i].pos=node[1][i].pos=i;
		}
		sort(node[0]+1,node[0]+n+1,cmp);
		sort(node[1]+1,node[1]+n+1,cmp);
		D();
		n=read();
	}
	return 0;
}

就是把区间按照左端点从小到大排序,左端点相同的就按照右端点从小到大排序。然后就直接。。

虽然这份代码贼好写,但是交上去WA了。然后就发现这个算法bug很明显,给一组数据:

3
1 1 3 3
1 2 2 3
2 2 2 2
0

这份代码输出IMPOSSIBLE,但是实际上是有解的。也就是说如果出现一个区间a左端点很大而区间长度很短,以至于存在左端点比它小而右端点大于它的区间b,这样可行解可能会是a区间取较小的数,b区间取较大的数。这份代码就很容易出问题。

于是我脑补了一会儿除了这种贪心思路以外,还能怎么做。

既然按左端点排序不行,那就只能按右端点排序了。按照右端点从小到大排序,右端点相同的情况。。。似乎左端点小的排在前或排在后没有多大影响。

每个区间取尽量靠左的位置,如果这个点被取过了就往右一步,一直到走到这个区间的右端点,如果还没有找到没被取过的点就:

IMPOSSIBLE

这样为什么是对的呢?
因为我们在上份代码WA的情况中发现一个规律:如果从左往右走,限制一个区间的最令人头疼的是右端点。如果右端点很靠右,那么这个区间为什么要先决策呢,反正以后也有机会(取点)。如果右端点很靠左,那么如果错过了这次,下次就可能再没机会(取点)了。

那为什么对于右端点相同的情况左端点的排列方式无所谓呢?自己脑补一下各种情况吧,很好想(其实是我懒。

然后代码:

//Serene
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
using namespace std;
const int maxn=5000+10;
int n,ans[2][maxn];
bool usd[maxn];

int aa;char cc;
int read() {
	aa=0;cc=getchar();
	while(cc<'0'||cc>'9') cc=getchar();
	while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();
	return aa;
}

struct Node{
	int l,r,pos;
}node[2][maxn];

bool cmp(const Node& a,const Node& b) {
	return a.r<b.r;
}

void D() {
	for(int p=0;p<2;++p) {
		memset(usd,0,sizeof(usd));
		for(int i=1;i<=n;++i) {
			int j=node[p][i].l;
			while(usd[j]&&j<=node[p][i].r) j++;
			if(j>node[p][i].r) {
				printf("IMPOSSIBLE\n");
				return ;
			}
			usd[j]=1;ans[p][node[p][i].pos]=j;
		}
	}
	for(int i=1;i<=n;++i) printf("%d %d\n",ans[0][i],ans[1][i]);
}

int main() {
	n=read();
	while(n) {
		for(int i=1;i<=n;++i) {
			node[0][i].l=read(); node[1][i].l=read();
			node[0][i].r=read(); node[1][i].r=read();
			node[0][i].pos=node[1][i].pos=i;
		}
		sort(node[0]+1,node[0]+n+1,cmp);
		sort(node[1]+1,node[1]+n+1,cmp);
		D();
		n=read();
	}
	return 0;
}
posted @ 2017-09-28 16:52  shixinyi  阅读(212)  评论(0编辑  收藏  举报