1. 程式人生 > >UVA 11134 Fabled Rooks 【貪心+問題分解】

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;
}