题目大意:
在一个n*n的棋盘上放置n个车,使得它们之间都不能互相攻击(任意两个车都不能同行或同列),并且,对于第i个车,限制它只能放在一个矩形区域内,(xli, yli),这个矩形的左上角顶点坐标是(xli, yli),右下角顶点坐标是 (xri, yri), 1 ≤ i ≤ n, 1 ≤ xli ≤ xri ≤ n, 1 ≤ yli ≤ yri ≤ n.
分析:
两个车相互攻击的条件是在同一行或列,可以看出,行和列是无关的,所以可以分解成两个一维问题。
问题就转化为在【1,n】选择n个数,使得第i个整数在闭区间【n1i,n2i】内。
思路:
法一:
一开始想的是:因为要把1-n分给这n个区间嘛,所以先把区间排序,然后按顺序依次分配1—n呗,排序规则是先按右端点r排序,再按左端点l排序,然后再依次赋值。这样我以为是正确的,但是WA了,后来想了想,确实不对,比如[1,8],[2,2]这样的话[2,2]在前面,那么[2,2]分配了1这个数,显然是不对的(其实这种排序思路是正确的,只是我的赋值方法是错的,只需要从每个区间的左端开始一次判断赋值就对了)。
法二:
我之后又换了中排序方法,先按l排序,再按r排序。这样从1-n直接赋值有个问题,有的区间跨度很大,但是它却提早赋了一个比较小的数,这样就错了,但是可以用优先队列来维护。
法三:
Lrj的代码给出的思路是:对于每个赋值的数【1,n】,每次找出合适的区间且b值最小,时间复杂度是O(n^2),这和法一排序的思想是一样的!
法一:0ms
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=5005;
typedef struct node
{
int l,r,id;
};
bool cmp(const node& a,const node& b)
{
if(a.r==b.r)return a.l<b.l;
return a.r<b.r;
}
bool vis[N];
int n;
bool solve(node* a,int* ans)
{
int j;
memset(vis,0,sizeof(vis));
for(int i=0;i<n;i++){
for(j=a[i].l;j<=a[i].r;j++)
if(!vis[j]){
vis[j]=1;
ans[a[i].id]=j;
break;
}
if(j>a[i].r)return 0;
}
return 1;
}
node x[N],y[N];
int ansx[N],ansy[N];
int main()
{
//freopen("f.txt","r",stdin);
while(~scanf("%d",&n)&&n){
for(int i=0;i<n;i++){
scanf("%d%d%d%d",&x[i].l,&y[i].l,&x[i].r,&y[i].r);
x[i].id=y[i].id=i;
}
sort(x,x+n,cmp);
sort(y,y+n,cmp);
if(!solve(x,ansx)||!solve(y,ansy)){
printf("IMPOSSIBLE
");continue;
}
for(int i=0;i<n;i++)
printf("%d %d
",ansx[i],ansy[i]);
}
return 0;
}
法二:优先队列 20ms
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int N=5005;
struct node
{
int l,r,id;
bool operator < (const node& rhs)const{
if(l==rhs.l)return r>rhs.r;
return l>rhs.l;
}
};
int n;
bool solve(node* a,int* ans)
{
priority_queue<node>q;
for(int i=0;i<n;i++)q.push(a[i]);
int cur=1;
while(!q.empty()){
node t=q.top();q.pop();
if(t.r<cur)return 0;
if(t.l<cur){
t.l=cur;
q.push(t);continue;
}
if(t.l!=cur)return 0;
ans[t.id]=cur;
cur++;
}
return 1;
}
node x[N],y[N];
int ansx[N],ansy[N];
int main()
{
//freopen("f.txt","r",stdin);
while(~scanf("%d",&n)&&n){
for(int i=0;i<n;i++){
scanf("%d%d%d%d",&x[i].l,&y[i].l,&x[i].r,&y[i].r);
x[i].id=y[i].id=i;
}
if(!solve(x,ansx)||!solve(y,ansy)){
printf("IMPOSSIBLE
");continue;
}
for(int i=0;i<n;i++)
printf("%d %d
",ansx[i],ansy[i]);
}
return 0;
}
法三:O(n^2) 150ms
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int N=5005;
bool solve(int* a,int* b,int* c,int n)
{
//memset(c,-1,sizeof(c)); 这里要用fill,因为c是指针
fill(c,c+n,-1);
for(int col=1;col<=n;col++){
int rook=-1,minb=n+1;
for(int i=0;i<n;i++)
if(c[i]<0&&b[i]<minb&&col>=a[i])rook=i,minb=b[i];
if(rook<0||col>minb)return 0;
c[rook]=col;
}
return 1;
}
int x1[N],x2[N],y1[N],y2[N],ansx[N],ansy[N],n;
int main()
{
//freopen("f.txt","r",stdin);
while(~scanf("%d",&n)&&n){
for(int i=0;i<n;i++){
scanf("%d%d%d%d",&x1[i],&y1[i],&x2[i],&y2[i]);
}
if(!solve(x1,x2,ansx,n)||!solve(y1,y2,ansy,n)){
printf("IMPOSSIBLE
");continue;
}
for(int i=0;i<n;i++)
printf("%d %d
",ansx[i],ansy[i]);
}
return 0;
}