• POJ3185 The Water Bowls(反转法or dfs 爆搜)


    POJ3185 The Water Bowls

      题目大意: 奶牛有20只碗摆成一排,用鼻子顶某只碗的话,包括左右两只在内的一共三只碗会反向,现在给出碗的初始状态,问至少要用鼻子顶多少次才能使所有碗都朝上

      一开始试了一下dfs,由于对dfs还是不太熟悉,先是用了一个数组b[i]来储存翻转后的状态,后来发现这个搜索的状态虽然类似背包,要么翻转,要么不翻转,但是翻转某个碗以后会对其他的也造成影响,所以这样这样做就错了,可以只用原来的数组就ok了

          在上述问题解决后,又因为胡乱剪枝导致wa了几次,一开始我想先对a[0]和a[19]进行判断是否为1,来确定是否需要翻转,这样的话a[19]那儿的1可能是翻转后形成的,这是进行翻转就回不到原来的状态,导致没法遍历2^n而wa,同时,对a[0]是否为1的判断也是错的,因为a[0]等于1时也可以通过翻转a[1]来实现a[0]=0的要求,a[19]=1也可翻转下一次的a[18].

    不用任何优化顶多2^20(10的6次方左右),也可以过,我最后是250ms左右过的.

      dfs代码:(毕竟dfs的题做的比较少,也就先用dfs来练手了)(先翻转再翻回来的dfs才是对的)

      

    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <cmath>
    #include <ctime>
    #include <iostream>
    #include <algorithm>
    #include <string>
    #include <vector>
    #include <deque>
    #include <list>
    #include <set>
    #include <map>
    #include <stack>
    #include <queue>
    #include <numeric>
    #include <iomanip>
    #include <bitset>
    #include <sstream>
    #include <fstream>
    using namespace std;
    #define rep(i,a,n) for (int i=a;i<n;i++)
    #define per(i,a,n) for (int i=n-1;i>=a;i--)
    #define in(n) scanf("%d",&(n))
    #define in2(x1,x2) scanf("%d%d",&(x1),&(x2))
    #define inll(n) scanf("%I64d",&(n))
    #define inll2(x1,x2) scanf("%I64d%I64d",&(x1),&(x2))
    #define inlld(n) scanf("%lld",&(n))
    #define inlld2(x1,x2) scanf("%lld%lld",&(x1),&(x2))
    #define inf(n) scanf("%f",&(n))
    #define inf2(x1,x2) scanf("%f%f",&(x1),&(x2))
    #define inlf(n) scanf("%lf",&(n))
    #define inlf2(x1,x2) scanf("%lf%lf",&(x1),&(x2))
    #define inc(str) scanf("%c",&(str))
    #define ins(str) scanf("%s",(str))
    #define out(x) printf("%d
    ",(x))
    #define out2(x1,x2) printf("%d %d
    ",(x1),(x2))
    #define outf(x) printf("%f
    ",(x))
    #define outlf(x) printf("%lf
    ",(x))
    #define outlf2(x1,x2) printf("%lf %lf
    ",(x1),(x2));
    #define outll(x) printf("%I64d
    ",(x))
    #define outlld(x) printf("%lld
    ",(x))
    #define outc(str) printf("%c
    ",(str))
    #define pb push_back
    #define mp make_pair
    #define fi first
    #define se second
    #define SZ(x) ((int)(x).size())
    #define mem(X,Y) memset(X,Y,sizeof(X));
    typedef vector<int> vec;
    typedef long long ll;
    typedef pair<int,int> P;
    const int dx[4]={1,0,-1,0},dy[4]={0,1,0,-1};
    const int INF=0x3f3f3f3f;
    const ll mod=1e9+7;
    ll powmod(ll a,ll b) {ll res=1;a%=mod;for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}
    const bool AC=true;
    
    int a[25],b[25],ans;
    bool flag;
    void dfs(int i,int cnt){
        if(i==20){
            flag=true;
            rep(j,0,20){
                if(a[j]==1) {
                    flag=false;
                    break;
                }
            }
            if(flag){
                ans=min(ans,cnt);
            }
            return;
        }
        else if(i==0){
        a[i]=!a[i];
        a[i+1]=!a[i+1];
        dfs(i+1,cnt+1);
        a[i]=!a[i];
        a[i+1]=!a[i+1];
        dfs(i+1,cnt); //此处也不用判断是否为1来剪枝,否则会wa
        }
         else if(i==19){
        a[i]=!a[i];
        a[i-1]=!a[i-1];
        dfs(i+1,cnt+1);
        a[i]=!a[i];
        a[i-1]=!a[i-1];
        dfs(i+1,cnt); //不要乱剪枝,否则翻转不回来
     }
     else{
         a[i]=!a[i];
        a[i-1]=!a[i-1];
        a[i+1]=!a[i+1];
        dfs(i+1,cnt+1);//先翻转,再翻回来
        a[i]=!a[i];
        a[i-1]=!a[i-1];
        a[i+1]=!a[i+1];
        dfs(i+1,cnt);
     }
     return ;
    }
    int main(){
        rep(i,0,20) {
            in(a[i]);
        }
        ans=INF;
        dfs(0,0);
        out(ans);
        return 0;
    }

     下面是反转法:考虑某个碗,翻转下一个碗.

           如果某个碗是0,则这个碗不需要考虑,继续考虑下一个碗,不断向前推进区间;

           如果某个碗是1,则必须翻转下一个碗,继续考虑下一个碗,不断向前推进区间;

           这样复杂度就是O(n),类似尺取法的思想;

            从对称性的角度考虑问题

           wa的注意了,不要通过判断a[0]=0来确定是否需要反转a[0],这样是错的如下面的情况: 0 0 1 0 0 0 0 0.....后面全是0,这时如果反转a[0]需要2步,不翻转a[0]需要12步(因为这个wa了n次),

        所以不管a[0]为什么值都要尝试一下是否需要反转a[0],从对称性来看,从左向右翻转,a[19]要么翻转,要么不翻转,

        所以翻转法有两种实现方式:从左向右翻转两次(反转a[0]或者不反转a[0])

                      从左向右翻转一次(不翻转a[0]),再从右向左翻转一次(不翻转a[19]);(让某个碗翻转的方式有两种)

     从左向右翻转两次(反转a[0]或者不反转a[0])

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    int a[20],b[20];
    void turn(int i){
        b[i-1]^=1; 
        b[i]^=1;
        if(i<19) b[i+1]^=1;
    }    
    int main(){
        int cnt1=0,cnt2=0;
        for(int i=0;i<20;i++) 
            scanf("%d",&a[i]);
        //反转b[0];
        memcpy(b,a,sizeof(a));
        b[0]^=1;
        b[1]^=1;
        cnt1++;       //此处要加1
        for(int i=0;i<19;i++){
            if(b[i]){
             turn(i+1);//考虑某个碗,反转其后面的碗
             cnt1++;
             }
        }
        if(b[19]) cnt1=0x3f3f3f3f;
        //不反转b[0];
        memcpy(b,a,sizeof(a));
        for(int i=0;i<19;i++){
            if(b[i]){
             turn(i+1);//考虑某个碗,反转其后面的碗
             cnt2++;
             }
        }
        if(b[19]) cnt2=0x3f3f3f3f;
        printf("%d
    ",min(cnt1,cnt2));
        return 0;
    }
        
        
        

    从左向右翻转一次(不翻转a[0]),再从右向左翻转一次(不翻转a[19]);

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    int a[20],b[20];
    void turn1(int i){
        b[i-1]^=1; 
        b[i]^=1;
        if(i<19) b[i+1]^=1;
    }    
    void turn2(int i){
        a[i+1]^=1; 
        a[i]^=1;
        if(i>0) a[i-1]^=1;
    }    
    int main(){
        int cnt1=0,cnt2=0;
        for(int i=0;i<20;i++) 
            scanf("%d",&a[i]);
        memcpy(b,a,sizeof(a));
        for(int i=0;i<19;i++){
            if(b[i]){
             turn1(i+1);//考虑某个碗,反转其后面的碗
             cnt1++;
             }
        }
        if(b[19]) cnt1=0x3f3f3f3f;
        for(int i=19;i>0;i--){
            if(a[i]){
             turn2(i-1);//考虑某个碗,反转其前面的碗
             cnt2++;
             }
        }
        if(a[19]) cnt2=0x3f3f3f3f;
        printf("%d
    ",min(cnt1,cnt2));
        return 0;
    }
        
        
        
  • 相关阅读:
    第三天 moyax
    mkfs.ext3 option
    write file to stroage trigger kernel warning
    download fomat install rootfs script
    custom usb-seriel udev relus for compatible usb-seriel devices using kermit
    Wifi Troughput Test using iperf
    learning uboot switch to standby system using button
    learning uboot support web http function in qca4531 cpu
    learngin uboot design parameter recovery mechanism
    learning uboot auto switch to stanbdy system in qca4531 cpu
  • 原文地址:https://www.cnblogs.com/akrusher/p/5353790.html
Copyright © 2020-2023  润新知