1. 程式人生 > >#417 Div2 Problem B Sagheer, the Hausmeister (DFS && 枚舉)

#417 Div2 Problem B Sagheer, the Hausmeister (DFS && 枚舉)

logs isp 分析 log else c++ sof span print

題意 : 給出一個 n (1?≤?n?≤?15)層的教學樓, 每一層樓包含 m (1?≤?m?≤?100)個房間, 另外每一層的兩邊還有樓梯口, 接下來有n 行每行有 m+2(包含樓梯口) 用0和1來表示這棟樓的信息, 0代表這個房間的燈沒亮, 1代表亮, 現在保安在整棟樓的左下角的樓梯口, 他的目的是關掉這棟樓所有的燈, 而且保安在上樓時他所在的當前樓層燈需全滅才能繼續上樓, 而且每經過一個房間和上下樓梯都需要消耗1分鐘, 問你最後最少需要多少分鐘才能將整棟樓的燈關掉(註意燈全滅的樓層可以不必理會)!還有保安在關掉所有燈後就不會進行任何移動操作了!

分析 : 這題的關鍵是保安上樓時的決策, 即若保安現在左樓梯, 那他到底是關燈後下一層通過右樓梯走上上一層(這時耗時就是m+1), 還是先去關掉這一層所有的燈再走回左邊樓梯上樓。由於這題樓層最多只有15層, 如果枚舉每一層保安所有可能的走法那也就是2^15次方的復炸度, 可以接受, 所以可以采用DFS來枚舉所有保安走法即可, 但是這裏需要註意樓頂的層數並不一定是n, 因為可能在某一層例如第k層以後, 上面的燈就全都是滅的, 那保安就沒必要繼續上樓了, 枚舉到第k層即可!

瞎想 : 可否貪心模擬?我一開始是考慮對於每一層保安所在的樓梯口進行貪心策略, 看通過哪一個樓梯口上樓消耗的時間更短, 但是掛在了第九個用例, 因為只考慮了當前樓層, 而沒有結合以後樓層的情況進行考慮, 所以並不是最優, 說到這裏, 這就有點DP的味道了!的確, 看了大佬們的代碼, 看到了很多用DP解決。

瞎搞 : 其實貪心是可以很快寫出來的, 又是沒有使用模塊化思想, 代碼又長又臭, Debug了挺久。還有就是又沒有考慮清楚當前的貪心策略會不會有BUG和沒有考慮清楚頂樓情況, 導致代碼寫出來比賽已經OVER了 /(ㄒoㄒ)/~~, 最後還錯了!!!

貪心錯誤做法:

技術分享
#include<bits/stdc++.h>
#define
LL long long #define ULL unsigned long long #define lowbit(i) (i&(-i)) using namespace std; const int INF = 0x3f3f3f3f; int main(void) { int G[20][1000]; int n, m; bool flag; int cnt = 0; scanf("%d%d", &n, &m); for(int i=1; i<=n; i++){ flag = false;
for(int j=1; j<=m+2; j++){ char ch; scanf("%c", &ch); if(isdigit(ch)) G[i][j] = ch-0; else j--; if(G[i][j]==1) flag = true; } if(flag && !cnt) cnt = i; } bool L = true; bool even; if((m+2)%2==0) even = true; else even = false; int ans; if(cnt==0) {puts("0");return 0;} else ans = n-cnt; //printf("%d %d", cnt, ans);puts(""); for(int i=n; i>=1; i--){ if(i==cnt){ if(L){ int temp = -1; for(int j=m+2; j>=1; j--){ if(G[i][j]==1){ temp = j; break; } } if(temp==-1); else{ ans+=temp-1; } }else{ int temp = -1; for(int j=1; j<=m+2; j++){ if(G[i][j]==1){ temp = j; break; } } if(temp==-1); else{ ans+=(m+2)-temp; } } break; } if(L){ int temp = -1; for(int j=m+2; j>=1; j--){ if(G[i][j]==1){ temp = j; break; } } if(temp==-1); else{ if(even){ if(temp>(m+2)/2){ ans+=m+1; L = false; }else{ ans+=temp-1; ans+=temp-1; } }else{ if(temp>((m+2)/2) + 1){ ans+=m+1; L = false; }else{ ans+=temp-1; ans+=temp-1; } } } }else{ int temp = -1; for(int j=1; j<=m+2; j++){ if(G[i][j]==1){ temp = j; break; } } if(temp==-1); else{ if(even){ if(temp<=(m+2)/2){ ans+=m+1; L = true; }else{ ans+=(m+2)-temp; ans+=(m+2)-temp; } }else{ if(temp<=((m+2)/2) + 1){ ans+=m+1; L = true; }else{ ans+=(m+2)-temp; ans+=(m+2)-temp; } } } } } printf("%d\n", ans); return 0; }
View Code

以下代碼枚舉做法, 由於有黏貼貪心時所寫的代碼, 所以又長又臭, 湊合著看吧

技術分享
#include<bits/stdc++.h>
#define LL long long
#define ULL unsigned long long
#define lowbit(i) (i&(-i))
using namespace std;
const int INF = 0x3f3f3f3f;
LL ans = INF;
int F;
int G[20][1000];
int n, m;
bool flag;
int cnt = 0;
void dfs(bool pre, bool now, int x, LL sum)//參數分別代表上一層所在的樓梯口位置, 和當前將要去往的樓梯口位置, 當前樓層數, 以及耗費了多少時間
{
    if(pre){//如果上一層是在左樓梯
        if(x==F){//如果在頂樓, 需要特殊處理
            int tmp = -1;
            for(int j=m+2; j>=1; j--){
                if(G[x][j]==1){
                    tmp = j;
                    break;
                }
            }
            if(tmp!=-1)sum+=tmp-1;
        }else{
            if(now){
                int tmp = -1;
                for(int j=m+2; j>=1; j--){
                    if(G[x][j]==1){
                        tmp = j;
                        break;
                    }
                }
                if(tmp!=-1){
                    sum += 2*(tmp-1);
                }
            }else{
                sum += m+1;
            }
        }
    }else{
        if(x==F){
            int tmp = -1;
            for(int j=1; j<=m+2; j++){
                if(G[x][j]==1){
                    tmp = j;
                    break;
                }
            }
            if(tmp!=-1) sum+=(m+2)-tmp;
        }else{
            if(now){
                sum += m+1;
            }else{
                int tmp = -1;
                for(int j=1; j<=m+2; j++){
                    if(G[x][j]==1){
                        tmp = j;
                        break;
                    }
                }
                if(tmp!=-1){
                    sum += 2*(m+2 - tmp);
                }
            }
        }
    }
    if(x!=F){
        dfs(now, true, x+1, sum);
        dfs(now, false, x+1, sum);
    }else{
        if(sum<ans) ans = sum;
        return;
    }
}
int main(void)
{
    scanf("%d%d", &n, &m);
    for(int i=n; i>=1; i--){
        flag = false;
        for(int j=1; j<=m+2; j++){
            char ch;
            scanf("%c", &ch);
            if(isdigit(ch)) G[i][j] = ch-0;
            else j--;
            if(G[i][j]==1) flag = true;
        }
        if(flag && !cnt) cnt = i;
    }
    bool L = true;
    if(cnt==0) {puts("0");return 0;}//所有樓層都是燈滅的
    else F = cnt;//記錄有效頂樓
    dfs(true, true, 1, 0);//從第一層去往左樓梯上樓
    dfs(true, false, 1, 0);//從第一層去往右樓梯上樓
    printf("%lld\n", ans+F-1);//每一層消耗的體力還要加上上樓梯花費的體力
    return 0;
}
View Code

#417 Div2 Problem B Sagheer, the Hausmeister (DFS && 枚舉)