• 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
    ");
    				return;
    			}
    			ans[p][node[p][i].pos]=i;
    		}
    	for(int i=1;i<=n;++i) printf("%d %d
    ",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
    ");
    				return ;
    			}
    			usd[j]=1;ans[p][node[p][i].pos]=j;
    		}
    	}
    	for(int i=1;i<=n;++i) printf("%d %d
    ",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;
    }
    
    弱者就是会被欺负呀
  • 相关阅读:
    java工程文件路径的问题
    to_char
    tnsname.ora
    Linux(Centos)快速搭建SVN
    /etc/profile不生效问题
    不同servlet版本的web.xml的头部信息
    The serializable class XXX does not declare a static final serialVersionUID field of type long的警告
    面试:第六章:面试题收集
    面试:第一章:java基础各种区别
    面试:第二章:各种框架和中间件以及缓存数据库
  • 原文地址:https://www.cnblogs.com/Serene-shixinyi/p/7607449.html
Copyright © 2020-2023  润新知