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

浙公网安备 33010602011771号