zoukankan      html  css  js  c++  java
  • 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;
    }
        
        
        
  • 相关阅读:
    三点定位
    vue中使用UEditor编辑器 -- 2
    网络编程——socket编程
    异常处理
    面向对象进阶
    初识面向对象,面向对象之继承、多态和封装
    Python常用的模块
    函数递归,匿名、内置行数,模块和包,开发规范
    闭包、装饰器decorator、迭代器与生成器、面向过程编程、三元表达式、列表解析与生成器表达式
    Python基础学习——文件操作、函数
  • 原文地址:https://www.cnblogs.com/akrusher/p/5353790.html
Copyright © 2011-2022 走看看