zoukankan      html  css  js  c++  java
  • YBT 5.4 状态压缩动态规划

    #loj 10170. 「一本通 5.4 例 1」骑士

    看数据范围n<=10,所以不是搜索就是状压dp,又因为搜索会超时所以用dp

    dp[i][k][j]表示现已经放到第i行,前面共有k个,这一行状态为j

    so,dp[i][k][j]=dp[i-1][k-num[j]][t]

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #define low_bit(x) x&-x;
    using namespace std;
    inline long long read()
    {
        long long f=1,ans=0;char c;
        while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
        while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
        return ans*f;
    }
    long long dp[11][1001][1001];//dp[i][j][k]表示前i行放k个且第i行的状态为j
    long long n,k,s[1001],num[1001];
    long long cont(long long x)
    {
        long long c=0;
        while(x!=0) 
        {
            x-=low_bit(x);
            c++;
        }
        return c;
    }
    int main()
    {
        n=read(),k=read();
        long long ans=0;
        for(long long i=0;i<(1<<n)-1;i++)
        {
            if((i&(i<<1)))continue;
                s[++ans]=i;
                num[ans]=cont(i);
        }
        dp[0][1][0]=1;
    //    for(long long i=1;i<=ans;i++) cout<<s[i]<<" ";cout<<endl;
        for(long long i=1;i<=n;i++)
        {
            for(long long j=1;j<=ans;j++)
            {
                for(long long kk=0;kk<=k;kk++)
                {
                    if(kk>=num[j])
                    {
                        for(long long t=1;t<=ans;t++)
                        {
                            if(s[t]&s[j]) continue;
                            if(s[t]&(s[j]<<1)) continue;
                            if(s[t]&(s[j]>>1)) continue;
                            dp[i][j][kk]+=dp[i-1][t][kk-num[j]]; 
    //                        cout<<dp[i][j][k]<<endl;
                        }
                    }
                }
            }
        }
        long long sum=0;
        for(long long i=1;i<=ans;i++) sum+=dp[n][i][k];
        cout<<sum;
    }
    View Code

    #loj 10171. 「一本通 5.4 例 2」牧场的安排

    一道比较普通的状压dp,关键点就是输入时候怎么处理荒草

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #define mod 100000000
    using namespace std;
    inline long long read()
    {
        long long f=1,ans=0;char c;
        while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
        while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
        return ans*f;
    }
    long long m,n;
    //dp[i][j]表示第i行用第j个状态
    //dp[i][j]+=dp[i-1][k]
    long long dp[20][10001]; 
    long long ans[20];//ans[i]表示第i行有ans[i]个状态
    long long s[20][10001];//s[i][j]表示第i行第j个状态 
    void init(long long h,long long t)
    {
    //    cout<<t<<endl;
        for(long long i=0;i<=(1<<n)-1;i++)
        {
            if((i&(i<<1))||(i&(i>>1))||(i&t)) continue;
            s[h][++ans[h]]=i;
        }
        return; 
    }
    int main()
    {
        m=read(),n=read();
        for(long long i=1;i<=m;i++)
        {
            long long s=0;
            for(long long j=1;j<=n;j++) 
            {
                long long x=read();
                s=(s<<1)+1-x;
            }
            init(i,s); 
        }
        for(long long i=1;i<=ans[1];i++) dp[1][i]=1;
    //    for(long long i=1;i<=m;i++)
    //    {
    //        cout<<ans[i]<<endl;
    //        for(long long j=1;j<=ans[i];j++) cout<<s[i][j]<<" ";
    //        system("pause");
    //    }
    //         
        for(long long i=2;i<=m;i++)
        {
            for(long long j=1;j<=ans[i];j++)
            {
                for(long long k=1;k<=ans[i-1];k++)
                {
    //                cout<<i<<" "<<j<<" "<<k<<" "<<s[i][j]<<" "<<s[i-1][k]<<endl;
                    if(s[i][j]&s[i-1][k]) continue;
    //                if((s[i][j]>>1)&s[i-1][k]) continue;
    //                if((s[i][j]<<1)&s[i-1][k]) continue;
                    dp[i][j]+=dp[i-1][k]%mod; 
                }
            }
        }
        long long sum=0;
        for(long long i=1;i<=ans[m];i++) sum+=dp[m][i]%mod,sum%=mod;
        cout<<sum%mod;
    }
    View Code

    #loj 10172. 「一本通 5.4 练习 1」涂抹果酱

    手打三进制运算即可

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #define mod 1000000
    using namespace std;
    inline long long read()
    {
        long long f=1,ans=0;char c;
        while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
        while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
        return f*ans;
    }
    long long n,m,k,a[6001],dg[10001];
    bool check(long long x)
    {
        memset(dg,0,sizeof(dg));
        long long ans=0;
        while(x!=0)
        {
            dg[++ans]=x%3;
            x/=3;
        }
        for(long long i=m;i>=2;i--)
            if(dg[i]==dg[i-1]) return false;
        return true;
    }
    long long s[50],ans;
    long long dg1[50],dg2[50];
    void init()
    {
        ans=0;
        long long sry=1;
        for(long long i=1;i<=m;i++) sry*=3;
        for(long long i=0;i<=sry-1;i++)
            if(check(i))
                s[++ans]=i;    
        return;
    }
    long long dp[10001][50];
    bool check2(long long x,long long y)
    {
        long long ans1=0,ans2=0;
        memset(dg1,0,sizeof(dg1));
        memset(dg2,0,sizeof(dg2));
        while(x!=0)
        {
            dg1[++ans1]=x%3;
            x/=3;
        }
        while(y!=0)
        {
            dg2[++ans2]=y%3;
            y/=3;
        }
        for(long long i=m;i>=1;i--)
            if(dg1[i]==dg2[i]) return false;
        return true;
    }
    long long cx(long long x)
    {
        for(long long i=1;i<=ans;i++)
            if(s[i]==x) return i;
    }
    long long book[51][51];
    int main()
    {
        n=read(),m=read();
        k=read();
        for(long long i=1;i<=m;i++) 
        {
            a[i]=read();
            a[i]-=1;
        }
        long long kk=1,sum=0;
        for(long long i=m;i>=1;i--)
        {
            sum+=a[i]*kk;
            kk*=3;
        }
        if(check(sum)==false)
        {
            cout<<0;
            return 0;
        }
        init();
        for(long long i=1;i<=ans;i++)
            for(long long j=i;j<=ans;j++)
                 book[i][j]=book[j][i]=check2(s[i],s[j]);
        memset(dp,0,sizeof(dp));
        for(long long i=1;i<=ans;i++) dp[1][i]=1;
        for(long long i=2;i<=k;i++)
            for(long long j=1;j<=ans;j++)
                for(long long k=1;k<=ans;k++)
                    if(book[j][k])
                    {
                        dp[i][j]+=(dp[i-1][k])%mod;
                        dp[i][j]%=mod;
    //                    cout<<"行:"<<i<<" 状态:"<<j<<" dp[i][j]:"<<dp[i][j]<<endl;
                    }
        long long xxxx=cx(sum);
        long long p=dp[k][xxxx]%mod;
        if(p==0)
        {
            cout<<0;
            return 0;
        }
        memset(dp,0,sizeof(dp));
        for(long long i=1;i<=ans;i++) dp[n][i]=1;
        for(long long i=n-1;i>=k;i--)
            for(long long j=1;j<=ans;j++)
                for(long long k=1;k<=ans;k++)
                    if(book[j][k])
                    {
                        dp[i][j]+=(dp[i+1][k])%mod;
                        dp[i][j]%=mod;
                    }
                        
        long long p1=dp[k][xxxx]%mod;
        if(p1==0)
        {
            cout<<0;
            return 0;
        }
        cout<<(p1*p)%mod;
        return 0;
    }
    View Code

    #loj 10173. 「一本通 5.4 练习 2」炮兵阵地

    因为联系是由三行,所以dp数组存行数外还要开两位存这一位和上一位的状态

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #define lowbit(x) x&-x
    using namespace std;
    inline int read()
    {
        int f=1,ans=0;char c;
        while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
        while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
        return ans*f;
    }
    int cont(int x)
    {
        int c=0;
        while(x!=0)
        {
            x-=lowbit(x);
            c++;
        }
        return c;
    }
    int ans[1101];
    int s[101][1101];
    int n,m;
    void init(int ha,int t)
    {
        for(int i=0;i<=(1<<m)-1;i++) 
        {
            if(i&(i<<1)) continue;
            if(i&(i<<2)) continue;
            if(i&(i>>1)) continue;
            if(i&(i>>2)) continue;
            if(i&t) continue;
            s[ha][++ans[ha]]=i;
        }
        return;
    }
    int dp[4][1101][1101];
    int main()
    {
        n=read(),m=read();
        for(int i=1;i<=n;i++)
        {
            char str;
            int t=0;
            for(int j=1;j<=m;j++) 
            {
                cin>>str;
                if(str=='P') t=(t<<1);
                else if(str=='H') t=(t<<1)+1;
            }
            init(i,t);
        }
        if(n==1) 
        {
            cout<<ans[1];
            return 0;
        }
        for(int i=1;i<=ans[2];i++)
        {
            for(int j=1;j<=ans[1];j++)
            {
                int s1=s[2][i],s2=s[1][j];
                if(s1&s2) continue;
                dp[2][i][j]=max(dp[2][i][j],cont(s1)+cont(s2));
            }
        }
        for(int i=3;i<=n;i++)
        {
            for(int j=1;j<=ans[i];j++)
            {
                for(int k=1;k<=ans[i-1];k++)
               {
                       if(s[i][j]&s[i-1][k]) continue;
                    for(int t=1;t<=ans[i-2];t++)
                    {
                        int s1=s[i][j],s2=s[i-1][k],s3=s[i-2][t];
                        if(s1&s2) continue;
                        if(s1&s3) continue;
                        if(s2&s3) continue;
                        dp[i%3][j][k]=max(dp[i%3][j][k],dp[(i-1)%3][k][t]+cont(s1));
                        
                    }
                }
            }
        }
        int sum=0;
        for(int i=1;i<=ans[n];i++)
            for(int j=1;j<=ans[n-1];j++) 
            {
                if(s[n][i]&s[n-1][j]) continue;
                sum=max(sum,dp[n%3][i][j]);    
            }
        cout<<sum;
    }
    /*
    5 4 
    PHPP 
    PPHH
    PPPP 
    PHPP 
    PHHP
    */
    View Code

    #loj 10174. 「一本通 5.4 练习 3」动物园

    这题的关键点就在小朋友只能看到5个数字

    所以只需要状压这5个数字就好了

    其实不需要断环为链

    因为你可以每次先假设一种状态关系到0(其实是n),1,2,3,4

    最后只需要输出关于n的这种最大状态

    每次看一下需不需要放

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    inline int read()
    {
        int f=1,ans=0;char c;
        while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
        while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
        return ans*f;
    }
    int n,c;
    int e,f,l;
    int g[10001][32],dp[10001][32]; 
    int main()
    {
        n=read(),c=read();
        for(int p=1;p<=c;p++)
        {
            e=read(),f=read(),l=read();
            int scared=0;
            for(int i=1;i<=f;i++)
            {
                int x=read();
                x=(x-e+n)%n;
                scared|=(1<<x);
            }
            int happy=0;
            for(int i=1;i<=l;i++) 
            {
                int x=read();
                x=(x-e+n)%n;
                happy|=(1<<x);
            }
            int sry=31;
            for(int i=0;i<=31;i++)
            {
                if((i&happy)||((sry^i)&scared))
                {
                    g[e][i]++;
    //                cout<<i<<endl;
                }
            }
        }
        int ans=0;
        for(int s=0;s<=15;s++)
        {
            for(int j=0;j<=31;j++) dp[0][j]=-(2<<30-1);
            dp[0][s<<1]=dp[0][s<<1|1]=0;
            for(int i=1;i<=n;i++)
                for(int ss=0;ss<=31;ss++)
                    dp[i][ss]=max(dp[i-1][(ss&15)<<1],dp[i-1][(ss&15)<<1|1])+g[i][ss];
            ans=max(ans,max(dp[n][s<<1|1],dp[n][s<<1]));
        }
        cout<<ans;
    }
    View Code
  • 相关阅读:
    log4net插入access自定义字段
    前端规范
    烤冷面项目进度文档
    响应式布局及bootstrap(实例)
    HTML嵌套规则
    前端规范2-CSS规范
    前端规范1-HTML规范
    入驻博客园
    .net中运用solr提升搜索效率(入门)
    .net 使用validator做数据校验
  • 原文地址:https://www.cnblogs.com/si-rui-yang/p/9520806.html
Copyright © 2011-2022 走看看