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

题目链接:http://codeforces.com/contest/812/problem/B

题意 : 给出一个 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
", 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
", ans+F-1);//每一层消耗的体力还要加上上楼梯花费的体力
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/qwertiLH/p/6940112.html