zoukankan      html  css  js  c++  java
  • 状压dp的另一种形式

      做的那么多都是一些比较怎么说呢,都是在数网格一类的题目之中,这些题目有些有点固定的套路,而一些需要状态压缩的题目呢,则么是真正对状态转移的考验。

    这道题呢,被彻底打脸了,以后一定要任性一点一道题做不出来就要坚持啃,不管你干什么,先a了再说。

    但这道题我是真的伤,拿头去写估计也想不出来最后的解法。

    第一眼,这不是很简单的dp么?设f[i]表示第i个状态得到的最大价值那么这个状态就是由i这个状态的所有子集所构成。当然本人哪想的出来什么子集直接暴力枚举了。

    复杂度2^n^2^n没错这就是复杂度。只能的30分。

    //#include<bits/stdc++.h>
    #include<iostream>
    #include<iomanip>
    #include<cstdio>
    #include<cstring>
    #include<string>
    #include<ctime>
    #include<cstdlib>
    #include<cmath>
    #include<queue>
    #include<deque>
    #include<vector>
    #include<set>
    #include<bitset>
    #include<cctype>
    #include<utility>
    #include<map>
    #include<algorithm>
    #include<stack>
    using namespace std;
    inline int read()
    {
        int x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    inline void put(int x)
    {
        x<0?putchar('-'),x=-x:0;
        int num=0;char ch[50];
        while(x)ch[++num]=x%10+'0',x/=10;
        num==0?putchar('0'):0;
        while(num)putchar(ch[num--]);
        putchar('
    ');return;
    }
    const int maxn=17;
    int n,state,t=0;
    int v[1<<maxn];//v[i]表示第i个状态的价值
    int f[1<<maxn];//f[i]表示到达第i个状态的最优解
    void dfs(int x,int sum,int now)
    {
        if(now==x){f[x]=max(f[x],sum);return;}
        for(int i=1;i<=x;i++)
        {
            if(now&i)continue;
            dfs(x,sum+v[i],now|i);
        }
    }
    int main()
    {
        //freopen("1.in","r",stdin);
        n=read();state=(1<<n)-1;
        for(int i=1;i<=state;i++)v[i]=read();
        dfs(state,0,0);
        //for(int i=1;i<=state;i++)dfs(i,0,0);
        put(f[state]);
        return 0;
    }
    View Code

    然后也没心情听那所谓的数学课,觉得是在浪费时间,学长也不讲什么那还不如自己学。

    所以干脆就一直想,然后一直没想到优化的方法,然后叫了个学长帮我看看,康神看一眼就秒a了。

    真的是强,帮我找出代码中TLE的原因的是wydalao 他说我的dfs应该枚举子集,对哦。

    这是康神打的递推枚举子集然后成功AC的代码,跑的挺快的。

    //#include<bits/stdc++.h>
    #include<iostream>
    #include<iomanip>
    #include<cstdio>
    #include<cstring>
    #include<string>
    #include<ctime>
    #include<cstdlib>
    #include<cmath>
    #include<queue>
    #include<deque>
    #include<vector>
    #include<set>
    #include<bitset>
    #include<cctype>
    #include<utility>
    #include<map>
    #include<algorithm>
    #include<stack>
    using namespace std;
    inline int read()
    {
        int x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    inline void put(int x)
    {
        x<0?putchar('-'),x=-x:0;
        int num=0;char ch[50];
        while(x)ch[++num]=x%10+'0',x/=10;
        num==0?putchar('0'):0;
        while(num)putchar(ch[num--]);
        putchar('
    ');return;
    }
    const int maxn=17;
    int n,state,t=0;
    struct node{
        int v;
        int num;
    }e[1<<maxn];
    //v[i]表示第i个状态的价值
    int f[1<<maxn];//f[i]表示到达第i个状态的最优解
    int count(int x){
        int res=0;
        for(;x;x>>=1){
            if(x&1) res++;
        }
        return res;
    }
    bool cmp(node a,node b){
        return a.num<b.num;
    }
    int main()
    {
        //freopen("1.in","r",stdin);
        n=read();state=(1<<n)-1;
        for(int i=1;i<=state;i++)f[i]=read(),e[i].v=i;
        for(int i=1;i<=state;i++)e[i].num=count(i);
        sort(e+1,e+state+1,cmp);
        for(int i=1;i<=state;i++){
            int s=e[i].v;
            for(int s1=s;s1!=0;s1=s&(s1-1)){
                int s2=s^s1;
                f[s]=max(f[s1]+f[s2],f[s]);
            }
        }
        //for(int i=1;i<=state;i++)dfs(i,0,0);
        put(f[state]);
        return 0;
    }
    View Code

    细节处理也很对。敬佩三尺,真强啊。然后我十分的不服。

    自己学了一下下枚举当前状态的子集,怒打了一个记搜,也算是A了这道题,自己思考的程度很深了,这道题没白费。

    //#include<bits/stdc++.h>
    #include<iostream>
    #include<iomanip>
    #include<cstdio>
    #include<cstring>
    #include<string>
    #include<ctime>
    #include<cstdlib>
    #include<cmath>
    #include<queue>
    #include<deque>
    #include<vector>
    #include<set>
    #include<bitset>
    #include<cctype>
    #include<utility>
    #include<map>
    #include<algorithm>
    #include<stack>
    using namespace std;
    inline int read()
    {
        int x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    inline void put(int x)
    {
        x<0?putchar('-'),x=-x:0;
        int num=0;char ch[50];
        while(x)ch[++num]=x%10+'0',x/=10;
        num==0?putchar('0'):0;
        while(num)putchar(ch[num--]);
        putchar('
    ');return;
    }
    const int maxn=17;
    int n,state,t=0;
    int v[1<<maxn];//v[i]表示第i个状态的价值
    int f[1<<maxn];//f[i]表示到达第i个状态的最优解
    int dfs(int x)
    {
        if(f[x]!=0)return f[x];
        for(int i=x;i;i=(i-1)&x)
        {
            if(i==x)continue;
            int s1=x^i;
            dfs(i);dfs(s1);
            f[x]=max(f[x],f[s1]+f[i]);
        }
        return f[x]=max(f[x],v[x]);
    }
    int main()
    {
        //freopen("1.in","r",stdin);
        n=read();state=(1<<n)-1;
        for(int i=1;i<=state;i++)v[i]=read();
        dfs(state);
        put(f[state]);
        return 0;
    }
    View Code

    代码中记搜和一些状态初始值刚好形成嵌套关系,我也不知道自己则么写的把细节处理的这么好,自己还是可以的。

    学长的枚举子集方法比较难一点这里不再赘述。放一下枚举子集的方法。

    for(int i=x;i;i=(i-1)&x)
    {
       int s1^i;      
    }

    i是当前集合的子集,s1是当前集合的补集。这样复杂度就大大降低了。

    这道题的话也是很简单自己想的了状压dp,但是状态的设置和转移打了几个h都整不好,最后是qydalao教的,但是我不认同他的状态转移,但是a了就是事实。

    //#include<bits/stdc++.h>
    #include<iomanip>
    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<ctime>
    #include<cstdlib>
    #include<cstring>
    #include<string>
    #include<set>
    #include<bitset>
    #include<queue>
    #include<deque>
    #include<stack>
    #include<cctype>
    #include<utility>
    #include<algorithm>
    #include<map>
    #include<vector>
    #define INF 214748364
    using namespace std;
    inline long long read()
    {
        long long x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    inline void put(long long x)
    {
        x<0?x=-x,putchar('-'):0;
        long long num=0;char ch[50];
        while(x)ch[++num]=x%10+'0',x/=10;
        num==0?putchar('0'):0;
        while(num)putchar(ch[num--]);
        return;
    }
    const int MAXN=70000;
    int n,m;
    int f[1<<17];//f[i]表示第i个编码形成所需要的最小次数。
    char a[102][102],an[202];
    int b[602],cnt=0,ans,maxx=10000000,sum=0,c[602],t=0;
    int p[18]={0,1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768};
    int contrast(int x,int y)
    {
        int xx=0;
        while(1)
        {
            if(x==0&&y==0)break;
            if((x&1)!=(y&1))xx++;
            x=x>>1;y=y>>1;
        }
        return xx;
    }
    int main()
    {
        //freopen("1.in","r",stdin);
        m=read();n=read();
        scanf("%s",an+1);
        for(int i=1;i<=n;i++)scanf("%s",a[i]+1);
        for(int i=m,j=1;i>=1;i--,j++)cnt+=an[i]=='1'?p[j]:0;
        for(int i=0;i<=(1<<m);i++)f[i]=INF;
        for(int i=1;i<=n;i++)
        {
            for(int j=m,t=1;j>=1;j--,t++)
            {
                b[i]+=a[i][j]=='1'?p[t]:0;
            }
        }
        for(int i=1;i<=n;i++)
        {    
            for(int j=1;j<=n;j++)
            {
                f[b[i]^b[j]]=min(f[b[i]^b[j]],1);
            }
            f[b[i]]=2;
        }
        for(int i=1;i<=n;i++)
            for(int j=0;j<(1<<m);j++)
            {
                if(f[j]>1000000)continue;
                f[j^b[i]]=min(f[j^b[i]],f[j]+1);
            }
        for(int i=(1<<m)-1;i>=0;i--)
        {
            if(f[i]!=INF)
            {
                int u=contrast(cnt,i);
                if(u==maxx){if(f[i]<sum)sum=f[i],ans=i;if(f[i]==sum)ans=min(ans,i);}
                if(u<maxx){maxx=u;sum=f[i];ans=i;}
            }
        }
        put(sum);puts("");
        while(ans)
        {
            if(ans&1)c[++t]=1;
            else c[++t]=0;
            ans=ans>>1;
        }
        if(t<m)t+=m-t;
        for(int i=t;i>=1;i--)put(c[i]);
        return 0;
    }
    View Code

    关键是状态转移之处,最后的细节处理当然是简单的了。

    这道题是本人自己亲自相出来的思路,那天可能太聪明了,导致推出了正解,看着数据范围是状压。

    也可以是状压,但是不免的是随机化搜索什么的,模拟退火好像也可以A了这道题。

    但是本人亲自相出的思路那肯定是不一样的。对思维的真实锻炼。

    大体思路就是设f[i][j]表示第i个状态到达了第j个节点所需费用的最小值。

    那么这样的话考虑填表法得出,枚举当前状态到达了哪个节点,由哪个节点到达这个节点的一堆状态的转移可以得出最优解。

    自己的思路,AC了就是很爽呢。

    //#include<bits/stdc++.h>
    #include<iomanip>
    #include<utility>
    #include<cctype>
    #include<vector>
    #include<deque>
    #include<map>
    #include<stack>
    #include<queue>
    #include<bitset>
    #include<set>
    #include<cstdlib>
    #include<algorithm>
    #include<iostream>
    #include<cstdio>
    #include<ctime>
    #include<cmath>
    #include<cstring>
    #include<string>
    #define INF 214748364.5
    inline int read()
    {
        int x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    inline void put(int x)
    {
        x<0?x=-x,putchar('-'):0;
        int num=0;char ch[50];
        while(x)ch[++num]=x%10+'0',x/=10;
        num==0?putchar('0'):0;
        while(num)putchar(num--);
        putchar('
    ');return;
    }
    const int MAXN=20;
    int n,s1,s2,w;
    double ans=INF,a[MAXN][MAXN];
    int x[MAXN],y[MAXN];
    int p[18]={0,1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536};
    double f[1<<17][17];//f[i][j]表示达到第i个状态时所在的节点那么答案就是MIN{f[(1<<n)-1][j]};
    int b[MAXN],t=0;
    double distance(int u1,int u2,int x1,int x2)
    {
        return sqrt(((u1-x1)*(u1-x1)*1.0+(u2-x2)*(u2-x2)*1.0)*1.0);
    }
    void getstate(int x)
    {
        int cnt=1;
        while(x)
        {
            if(x&1)b[++t]=cnt;
            x=x>>1;cnt++;
        }
    }
    double min(double x,double y){return x<y?x:y;}
    int main()
    {
        //freopen("1.in","r",stdin);
        n=read();
        for(int i=1;i<=n;i++){x[i]=read();y[i]=read();}
        x[0]=read();y[0]=read();
        for(int i=0;i<=(1<<n);i++)for(int j=0;j<=n;j++)f[i][j]=INF;
        f[0][0]=0;
        for(int i=0;i<=n;i++)
            for(int j=0;j<=n;j++)
                a[i][j]=distance(x[i],y[i],x[j],y[j]);
        for(int i=1;i<(1<<n);i++)
        {
            t=0;
            getstate(i);
            for(int j=0;j<=t;j++)
                for(int k=0;k<=t;k++)
                {
                    if(b[j]!=b[k])f[i][b[j]]=min(f[i][b[j]],f[i-p[b[j]]][b[k]]+a[b[k]][b[j]]);
                }
        }
        for(int i=0;i<=n;i++)ans=min(ans,f[(1<<n)-1][i]);
        printf("%.2lf",ans);
        return 0;    
    }
    View Code

    状压dp学的还行,自己dp的水平也在不断上涨呢,觉得自己越来越强了。

    加油!

  • 相关阅读:
    设计模式 go语言实践-5 外观模式
    .net 5 preview发布
    设计模式 Vs实践-4 桥接模式
    设计模式 Vs实践-3 装饰器模式
    PowerDesign字段和中文名切换显示
    设计模式 Vs实践-2 抽象工厂模式
    设计模式 Vs实践-1 工厂模式
    环境变量path的值大于1024的解决办法
    powshell 输出字符编码的问题,设置为utf-8
    模拟真实点击click,专门对付clickoutside
  • 原文地址:https://www.cnblogs.com/chdy/p/10257008.html
Copyright © 2011-2022 走看看