UVA 11134 Fabled Rooks(贪心法,区间与选点问题)

本文介绍了一种基于贪心算法的区间选择方法,通过优先选择区间的右边界来优化放置点的过程,确保了每个点都能被有效放置,并显著提高了算法效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

x轴,y轴不相关,所以分开考虑

贪心法  可以这样思考,不同于定区间选点,而是定点选区间,那么怎么对区间排序最优?

假设前提是从1开始取点放,对于其中x这个点,假设有很多区间[a,b]包含x这个点,那么对于那么多的区间我们只要选取b最小的就好,注意是在包含x的区间中选!!

但为了加快选中的概率,再加一条规则:b相同时取a最小的区间,毕竟区间越长包含x的概率大;

再理一遍:

1) b最小 : 因为1~x-1都是放好了的已经,而x+1~n还未放位置,所以我们要给后来的多多留位置; 

2)b相同时取a最小的区间 :对于b相同,相比更长的区间覆盖到x的几率更大,所以我们要排在前面;

综上[a,b]区间要按b优先排序,b相同时a取小;

另外,因为是从1开始放点,如果出现一个区间a<x&& b<x,肯定是问题无解的,因为前1~x-1的点还没选走这个区间,那么以后肯定没有任何一个点能放在这个区间。

比如说 [1,1],[2,2],[1,3]  按a大小排序[1,1],[1,3],[2,2] 是错误的而按b大小排序[1,1],[2,2], [1,3]才是对的

代码如下:

#include<iostream>
#include<vector>
#include<queue>
#include<numeric>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<ctime>
using namespace std;
const int maxn=5000+5;
int n,x1[maxn],x2[maxn],y1[maxn],y2[maxn],x[maxn],y[maxn];

bool solve(int* a,int *b,int* c)   //[a,b] 
{
	memset(c,-1,sizeof(int)*n);  //  a[i] <= col <= b[i]
	/*fill(c,c+n,-1)  是对每一个int大小内存块初始化,而memset是对每个字节*/ 
	for(int col=1;col<=n0;col++)  //在1~n给每个区间放个root 
	{
		int root=-1,minb=n+1;
		for(int i=0;i<n;i++)
			if(c[i]<0&&a[i]<=col&& b[i]<minb) root=i,minb=b[i];//对于每个未放的区间选b最小的
	        if(root<0||minb<col) return false;
	  //if(放不了了||col为要放的位置>b即col在区间[a,b]外)   
		c[root]=col;
	}
	return true;
}

int main()
{
	while(scanf("%d",&n)==1&&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,x)&&solve(y1,y2,y))
		for(int i=0;i<n;i++) printf("%d %d\n",x[i],y[i]);
		else printf("IMPOSSIBLE\n");
	}
}



优化一下,直接按上述规则排序后再选区间,更加省时间;

RunTime :从140ms到20ms

#include<iostream>
#include<vector>
#include<queue>
#include<numeric>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<ctime>
using namespace std;
const int maxn=5000+5;
int n,x[maxn],y[maxn];
struct point
{
	int a,b,p;  //[a,b]   第p个点要放置的区间 
}x1[maxn],y1[maxn];

bool cmp(const point& a,const point& b)
{
	if(a.b!=b.b) return a.b<b.b;
	else return a.a<b.a;
}


bool solve(point* x,int* c)   
{
	sort(x,x+n,cmp);
	memset(c,-1,sizeof(int)*n);  
	for(int col=1;col<=n;col++) 
	{
		int root=-1,b=n+1;
		for(int i=0;i<n;i++)
		if(c[x[i].p]<0&&x[i].a<=col) 
		{
			root=i,b=x[i].b;
			break;
		}
		if(root<0||b<col) return false;
		c[x[root].p]=col;
	}
	return true;
}

int main()
{
	while(scanf("%d",&n)==1&&n)
	{
		for(int i=0;i<n;i++)
		scanf("%d%d%d%d",&x1[i].a,&y1[i].a,&x1[i].b,&y1[i].b),x1[i].p=y1[i].p=i;
		if(solve(x1,x)&&solve(y1,y))
		for(int i=0;i<n;i++) printf("%d %d\n",x[i],y[i]);
		else printf("IMPOSSIBLE\n");
	}
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值