题目链接:https://cn.vjudge.net/problem/UVA-11134;
题意:
在一个n*n的棋盘中放置 n个棋子,每个棋子有固定的放置范围,在满足放置范围的情况下,放置的棋子横向和纵向不能有第二个棋子;输出棋子的放置位置或者 impossible;
思路:
解这题的关键就是问题的分解,如果能想到问题的分解方法的话,就比较容易做了;因为之前也做过类似这种方格的题目,想到的方法就是分解成几个小方块,看看能不能把小问题的答案合并成大问题的答案,显然这种思路是错误的,因为有很多需要分开讨论的情况;如果你在分析一个问题的时候,有多种情况需要分开讨论求解的话,一般这种思路都不是最好的,或者说是错误的;平时做题的时候一定积累一些判断思路对错的经验,不然在时间上会很吃亏,发现思路错了就再重新想一个,而不是去想怎么解决各自各样的复杂情况;
问题的分解:
题目要求横向和纵向不能有第二个棋子;那么n*n的棋盘中放n个棋子,也就意味着横坐标每个点都只有一个棋子,同样的纵坐标也是;那么我可以优先处理横向放置方法,然后再处理纵向的放置方法;如果横向能够满足放置的条件,然后去处理纵向的;这样分解的好处是什么?我们在不分解问题之前,肯定有很多人在模拟放棋子的时候是同时考虑横向和纵向的,比如说我放一颗棋子,我先考虑在棋子的放置范围内,横向有没有其他的棋子,然后再看看纵向有没有其他棋子,这样的模拟方法写起代码来及其麻烦,而且还有后效性;这也是我之前想的,把大方块分成小方块的方法不可行的原因(没有解决主要矛盾);既然同时考虑会很麻烦,那分开考虑是否可行?
贪心:
横向放置和纵向放置都是用贪心实现的(这里只说横向就行了);(我在之前的博客也讲过,因为贪心大多都是根据排序方案优先处理某些数据实现的,如果要去检查自己的贪心策略是否可行,一定要从排序方案开始检查,检查的方法就是考虑排序的过程中,两者判断条件相同的情况下,是否取任意一个都不会影响最终结果,如果会的话,就要考虑如何修改排序方案了)
///////// 下面这两段的内容是写给自己看的,可以直接跳过;
先说说第一个 WA的贪心策略: 根据横向的放置范围的左值从小到大排序,如果左值大小相等再根据右值从小到大排序;咋一看好像可行,但是如果一个放置范围小的棋子的左值比一个范围大的左值大,那么那个范围大的会优先处理;导致的情况可能是,范围大的棋子放完后,范围小的就没棋子可以放了,然而范围大的虽然放完,但是能放的区域还有很多,如果优先放范围小的,然后再放范围大的就有可能同时满足两个棋子的放置,显然这种贪心并不正确;
第二个WA的贪心策略: 既然上一个策略因为范围的问题而导致错误,那么就考虑范围的排序;优先放置范围小的棋子,然后再考虑范围大的,这才有可能容纳更多的棋子;然后检查一下,如果范围相同的情况下,是否取任意一个数据优先处理都是正确的?我们看看这 3个数据 1 2 | 2 3 | 1 2 ,这三个数据的范围是相同的,如果我们根据给出的顺序处理,那么 第1 我们会优先 看看1能不能放,可以就标记一下,然后看第二个 2是否能放,是就标记一下,然后再看第三个,显然没位置可以放了,但是如果我们让第二个棋子放在 3这个位置,第三个就能放棋子了;这里的检查方法也是我上面说的那个,一定要考虑,如果排序条件相同,一定要保证取任意一个都不影响正确答案;
根据第二个策略;想想错误的原因:第二个棋子虽然 2和3都能放,但是,2却是第三个棋子的右界,第二个棋子的右界比第三个的要大,说明第二个棋子还有后移一位放置棋子的可能;
////////// 上面内容可跳过
总结上面的错误,正确的贪心策略就是根据棋子的右界从小到大排序;
下面是代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<queue>
#include<map>
#include<stack>
#include<sstream>
#include<vector>
using namespace std;
#define IOS ios::sync_with_stdio(false); cin.tie(0);
typedef long long ll;
const int Maxn = 5100;
const long long LINF = 1e18;
const int INF = 0x3f3f3f3f;
struct point {
int hx,hy,tx,ty,xx,yy,pos;
} a[Maxn];
int vis[Maxn];
bool hang (const point &a1, const point &a2) {
return a1.ty < a2.ty;
}
bool lie (const point &a1, const point &a2) {
return a1.tx < a2.tx;
}
bool cmp (const point &a1, const point &a2) {
return a1.pos < a2.pos;
}
int main (void)
{
int n;
while (scanf("%d",&n) != EOF) {
if(!n) break;
for (int i = 1; i <= n; ++i) {
scanf("%d%d%d%d",&a[i].hx,&a[i].hy,&a[i].tx,&a[i].ty);
a[i].pos = i;
}
sort(a+1,a+n+1,hang);
memset(vis,0,sizeof(vis));
bool flag;
for (int i = 1; i <= n; ++i) {
flag = false;
for (int j = a[i].hy; j <= a[i].ty; ++j) {
if(!vis[j]) {
a[i].yy = j;
flag = true; vis[j] = 1;
break;
}
}
if(!flag) {
printf("IMPOSSIBLE\n"); break;
}
}
if(!flag) continue;
sort(a+1,a+n+1,lie);
memset(vis,0,sizeof(vis));
for (int i = 1; i <= n; ++i) {
flag = false;
for (int j = a[i].hx; j <= a[i].tx; ++j) {
if(!vis[j]) {
a[i].xx = j;
flag = true; vis[j] = 1;
break;
}
}
if(!flag) {
printf("IMPOSSIBLE\n"); break;
}
}
if(!flag) continue;
sort(a+1,a+n+1,cmp);
for (int i = 1; i <= n; ++i) {
printf("%d %d\n",a[i].xx,a[i].yy);
}
}
return 0;
}