0%

UVA11134题:传说中的车(问题分解)

题目传送门

https://vjudge.net/problem/UVA-11134

题意

在一个n×n的国际象棋棋盘中,有n个国际象棋中的车,然后题目会给出n块区域,要求这n个车既要在这n个区域中,又要使车不能互相吃(两两不在同一行或不在同一列)。

分析(建议先去看我前面的博客“贪心与区间问题”再来攻克此题)

这道题非常像回溯法的n皇后问题,但是仔细一想你就会发现事情不对。

假设它个你的区域非常大怎么办,难道你要把所有的区域每个格子都dfs一遍,况且n皇后问题顶多也就8行8列,这玩意儿给你个最大值5000!必定超时!所以立即推——>放弃回溯。

那怎么做呢?其实最关键的就在于行和列互不相等,我们能不能把行和列分解成两个子问题来解答呢?答案是可以的,我们完全可以分两步,第一步算出符合条件的所有行坐标,第二步再算出所有符合条件的列坐标,然后我们就可以将两个坐标合并得到答案了。

是不是很神奇,请诸位仔细思考这个分解的过程。这样我们就把一个二维的问题分解成了两个一维的问题了,仔细想想接下来的问题是不是跟前面“贪心与区间问题”这篇文章里差不多呢,希望大家自己思考一下。

然后我们来讲一下分解后的每一步该怎么求呢,这就要用到贪心算法,还记得我们的贪心与区间问题吗,这里也就是借鉴了那里其中一道题目的创意。就是我们把所有的区间按照右端点从小到大排序,然后我们在尽量把车往区间的左边放,如果整个区间都放不下了,那就输出“impossible”

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;
typedef pair<int,int> P;
struct pos{
int left,right,num;
};
int vis[5000];

int main(){
int n;
while(cin>>n&&n){
pos* a=new pos[n+10];
pos* b=new pos[n+10];
P* ans=new P[n+10];
memset(vis,0, sizeof(vis));
for(int i=0;i<n;i++) {
cin >> a[i].left >> b[i].left >> a[i].right >> b[i].right;
a[i].num=b[i].num=i;
}
sort(a,a+n,[](pos A,pos B){ return A.right<B.right;});
sort(b,b+n,[](pos A,pos B){ return A.right<B.right;});
int flag=1;
for(int i=0;i<n&&flag;i++){
int t=a[i].left;
while(vis[t])
t++;
if(t<=a[i].right){
vis[t]=1;
ans[a[i].num].first=t;
}
else
flag=0;
}
memset(vis,0,sizeof(vis));
for(int i=0;i<n&&flag;i++){
int t=b[i].left;
while(vis[t])
t++;
if(t<=b[i].right){
vis[t]=1;
ans[b[i].num].second=t;
}
else
flag=0;
}
if(!flag)
cout << "IMPOSSIBLE" << endl;
else{
for(int i=0;i<n;i++)
cout << ans[i].first << ' ' << ans[i].second << endl;
}
}
return 0;
}