传说中的车(Fabled Rooks,UVa 11134)

AC通道:
https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=2075

传说中的车

Description

你的任务是在 nn 的棋盘上放 nn5000 个车,使得任意两个车不相互攻击,且第 i 个车在一个给定的矩形 Ri 之内。
4 个整数 xliylixriyri1xlixrin1yliyrin 描述第 i 个矩形。
其中 xliyli 是左上角坐标, xriyri 是右下角坐标。
则第 i 个车的位置 xy 必须满足 xlixxriyliyyri
如果无解,输出 IMPOSSIBLE
否则输出 n 行,依次为第 12n 个车的坐标。

Sample Input

8
1 1 2 2
5 7 8 8
2 2 5 5
2 2 5 5
6 3 8 6
6 3 8 5
6 3 8 8
3 6 7 8
8
1 1 2 2
5 7 8 8
2 2 5 5
2 2 5 5
6 3 8 6
6 3 8 5
6 3 8 8
3 6 7 8
0

Sample Output

1 1
5 8
2 4
4 2
7 3
8 5
6 6
3 7
1 1
5 8
2 4
4 2
7 3
8 5
6 6
3 7

Solution

我们可以发现,这个题目与八皇后问题的不同之处在于,它没有限制对角线的情况。

所以我们可以采用问题分解的方法。
把行与列分开,把原问题转化为在区间 [1n] 中选择 n 个整数,使得第 i 个整数在区间 [xlixri] 或区间 [yliyri] 之中。

首先,我们可以想到一个贪心的方法:将区间按照左端点排个序,然后对每个区间,从左到右找到第一个未被放置的点。

可惜错了。

反例:[1,4],[1,4],[1,4],[2,3]。
按照我们的算法,选择 123 ,那么第四个区间就无法选择了。

我们发现这是一个大区间包含小区间的情况。
在这种情况下,我们需要优先处理小区间。
因为选择大区间的时候,可能会取到大区间和小区间相交的地方。
而小区间右边,也在大区间里面的地方没有被取到。

如何实现呢?

我们把区间按照右端点从小到大排个序。
然后对每个区间,从左到右找到第一个未被放置的点,就可以解决这个问题。
这个过程又可以使用并查集完成(也可以不用,但用并查集能加快速度)。
并查集的过程的具体实现方法请见我的另一篇博客:
http://blog.csdn.net/xy20130630/article/details/50562138。

Code

[cpp]
  1. #include <iostream>  
  2. #include <cstdio>  
  3. #include <algorithm>  
  4.   
  5. using namespace std;  
  6.   
  7. struct xy{  
  8.     int l,r,num;  
  9. };  
  10.   
  11. xy xx[5010],yy[5010];  
  12. int x[5010],y[5010];  
  13. int fa1[5010];  
  14. int fa2[5010];  
  15. int n;  
  16.   
  17. int find1(int x){  
  18.     int tmp=x,pre;  
  19.     while(tmp!=fa1[tmp])tmp=fa1[tmp];  
  20.     while(x!=tmp){  
  21.         pre=fa1[x];  
  22.         fa1[x]=tmp;  
  23.         x=pre;   
  24.     }  
  25.     return tmp;  
  26. }  
  27.   
  28. int find2(int x){  
  29.     int tmp=x,pre;  
  30.     while(tmp!=fa2[tmp])tmp=fa2[tmp];  
  31.     while(x!=tmp){  
  32.         pre=fa2[x];  
  33.         fa2[x]=tmp;  
  34.         x=pre;   
  35.     }  
  36.     return tmp;  
  37. }  
  38.   
  39. bool cmp(xy a,xy b){  
  40.     return a.r<b.r;  
  41. }  
  42.   
  43. inline int Max(int x,int y){  
  44.     return x>y?x:y;  
  45. }  
  46.   
  47. int main(){  
  48.     while(scanf(“%d”,&n)!=EOF&&n){  
  49.         for(int i=1;i<=n;i++){  
  50.             scanf(”%d%d%d%d”,&xx[i].l,&yy[i].l,&xx[i].r,&yy[i].r);  
  51.             fa1[i]=fa2[i]=i;  
  52.             xx[i].num=yy[i].num=i;  
  53.         }  
  54.         fa1[0]=fa2[0]=0;  
  55.         fa1[n+1]=fa2[n+1]=0;  
  56.         sort(xx+1,xx+n+1,cmp);  
  57.         sort(yy+1,yy+n+1,cmp);  
  58.         for(int i=1;i<=n;i++){  
  59.             bool flag=true;  
  60.             for(int j=find1(xx[i].l);j<=xx[i].r;j=find1(j)){  
  61.                 if(j==0)break;  
  62.                 x[xx[i].num]=j;  
  63.                 fa1[j]=j+1;  
  64.                 flag=false;  
  65.                 break;  
  66.             }  
  67.             if(flag){printf(“IMPOSSIBLE\n”);goto nxt;}  
  68.         }  
  69.         for(int i=1;i<=n;i++){  
  70.             bool flag=true;  
  71.             for(int j=find2(yy[i].l);j<=yy[i].r;j=find2(j)){  
  72.                 if(j==0)break;  
  73.                 y[yy[i].num]=j;  
  74.                 fa2[j]=j+1;  
  75.                 flag=false;  
  76.                 break;  
  77.             }  
  78.             if(flag){printf(“IMPOSSIBLE\n”);goto nxt;}  
  79.         }  
  80.         for(int i=1;i<=n;i++)printf(“%d %d\n”,x[i],y[i]);  
  81. nxt:;  
  82.     }  
  83.     return 0;   
  84. }  

你可能感兴趣的:(并查集,贪心,问题分解)