UVA 11134 Fabled Rooks 【貪心+問題分解】
題意:
在一個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;
}