紫书 例题8-4 UVa 11134(问题分解 + 贪心)

 这道题目可以把问题分解, 因为x坐标和y坐标的答案之间没有联系, 所以可以单独求两个坐标的答案
 我一开始想的是按照左区间从小到大, 相同的时候从右区间从小到大排序, 然后WA
 去uDebug找了数据, 发现这组数据过不了
 3
 1 1 3 3
 1 1 3 3
 2 2 2 2
 正确输出是
 1 1
 3 3
 1 1
 2 2 
 我输出 IMPOSSIBLE
 我发现当有包含关系的时候, 会先处理大区间而把小区间应该放的点覆盖掉了。所以我这个方法是不行滴, 然后就暂时不知道怎么改了。

 之后我去看了他人的博客发现是按右区间从小到大排序, 左区间从大到小排序,然后从左端点开始放, 放过就跳过, 如果放不了就无解。这种排序方式, 有包含关系的时候, 小的会先处理, 不会出现大的先处理然后覆盖了小区间的情况。

所以这类区间的问题排序方式应该是这种排序方式比较通用, 紫书前面讲贪心法的时候都是这么排序的。
除此之外要考虑区间包含问题, 这道题的包含问题排序的时候就直接解决了。

#include<cstdio>
#include<cstring>
#include<algorithm>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
using namespace std;

const int MAXN = 5123;
struct node
{
	int l, r, id;
}x[MAXN], y[MAXN];
int ansx[MAXN], ansy[MAXN], vis[MAXN], n, ok;

bool cmp(node x, node y)
{
	if(x.r != y.r)
		return x.r < y.r;
	return x.l > y.l;
}

bool work(node *t, int p)  //两次求坐标可以写在一起 
{
	memset(vis, 0, sizeof(vis));
	sort(t, t + n, cmp);
	REP(i, 0, n)
	{
		int pos = t[i].l;
		while(1)
		{
			if(!vis[pos]) 
			{
				vis[pos] = 1;
				if(p == 0) ansx[t[i].id] = pos;
				else ansy[t[i].id] = pos;
				break;
			}
			else pos++;
			if(pos > t[i].r) return false;
		}
	}
	return true;
}

int main()
{
	while(~scanf("%d", &n) && n)
	{
		REP(i, 0, n)
		{
			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(work(x, 0) && work(y, 1))
		{
			REP(i, 0, n)
				printf("%d %d\n", ansx[i], ansy[i]);
		}
		else puts("IMPOSSIBLE");
	}
	return 0;	
} 

posted @ 2018-04-29 12:02  Sugewud  阅读(109)  评论(0编辑  收藏  举报