#417 Div2 Problem B Sagheer, the Hausmeister (DFS && 枚舉)
題意 : 給出一個 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> #defineView CodeLL 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; }
以下代碼枚舉做法, 由於有黏貼貪心時所寫的代碼, 所以又長又臭, 湊合著看吧
#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 && 枚舉)