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; }
弱者就是会被欺负呀