信息学奥赛一本通 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;
}

#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;
}

#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;
}

#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
*/

#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;
}

原文地址:https://www.cnblogs.com/si-rui-yang/p/9520806.html

时间: 2024-08-08 00:41:29

信息学奥赛一本通 5.4 状态压缩动态规划的相关文章

信息学奥赛一本通 5.1 区间类动态规划

石子合并[loj 10147] /* dp[i][j]=max or min(dp[i][j],dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]) i<=k<j */ #include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> using namespace std; inline int re

奇妙的算法—状态压缩动态规划

华电北风吹 天津大学认知计算与应用重点实验室 日期:2015/8/27 由于代码未调试完全正确论文草稿呈现 poj上一道需要用到状态压缩动态规划,链接http://poj.org/problem?id=3254 网上看到有很多人写出了代码,参考了一个带备忘的自顶向下的动态规划解法,自己写了一个有底向上的动态规划解法 有底向上: #include<iostream> #include<math.h> #include<ostream> #include<fstrea

状态压缩动态规划总结

状态压缩是一个很广的概念,在OI中也有很多的应用,当我们把他应用到动态规划中,可以用来精简状态,节约空间,也方便转移.最常见的就是用二进制来表是状态,利用各种位移运算,就可以实现\(O(1)\)的转移.状压DP适用于“窄棋盘”上的DP,否则状态太多无法存下. POJ1185 炮兵阵地 题意:给一个\(N \times M\)的地形盘,有平原和山坡,要求在平原上部署尽量多的炮(攻击范围2),使得不互相攻击. 数据范围:N <= 100:M <= 10,符合条件.如何表示状态?按行DP,一个二进制

状态压缩动态规划 -- 炮兵阵地

司令部的将军们打算在N*M的网格地图上部署他们的炮兵部队,一个N*M的地图由N行M列组成,地图的每一格可能是山地(用"H" 表示),也可能是平原(用"P"表示),如下图.在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队):一支炮兵部队在地图上的攻击范围如图中黑色区域所示: 如果在地图中的灰色所标识的平原上部署一支炮兵部队,则图中的黑色的网格表示它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格.图上其它白色网格均攻击不到.从图上可见炮兵的攻击

状态压缩动态规划 -- 棋盘问题 POJ 1321

一个 N * N 的棋盘上面,有些格子不能放,放置 M 的棋子, 每两个棋子不能在同一行或者同一列,问有多少种放法 DFS太慢,用SCR好点点 Python 只有 22 行,其实可以更短,但是得排成很长很长的一行 while True: table = [ [ 0 for j in range( 300 ) ] for i in range( 12 ) ] table[0][0] = 1 boardsize, chessnum = map( int, raw_input().split() )

状态压缩动态规划 -- 旅行商问题

旅行商问题: N个点(N<16)的带权有向图D,求一条路径,使得这条路经过每个点恰好一次, 并且路径上边的权值和最小(或者最大),或者求一条具有这样性质的回路. 状态压缩: 将二进制表示十进制数N的点集,比如: 10 = 0000000000001010 代表第1和3个点已经路过 18 = 0000000000010010 代表第1和4个点已经路过 一个整数就是一个点集, dp_arr[binary][to_]代表经过点集 binary 中,当前终点为to_, 且路径最短的值,若该状态不存在就是

状态压缩动态规划 -- 多米诺骨牌

用1*2 的骨牌通过组合拼成 m * n 的大矩形,问有几种拼法. 题目链接:http://poj.org/problem?id=2411 状态转移: 1.由于上一行的该列竖直放置骨牌为 0,影响到当前行的该列,当前行的该列为 1 2.当前行骨牌横放,上一行骨牌横放, 都为11 3.上一行该列置为 1,当前行当前列立着放为 0 #include <iostream> #include <cstring> using namespace std; #define MAXSIZE 12

状态压缩-动态规划

状态压缩-动态规划 ---By蒟蒻鱼 用二进制表示城市的到达(规划的)状态 每一个二进制数都代表一个唯一的十进制数 预备知识 位运算 优先级 ~ << 和 >> & ^ | 按位与 & 全一则一,否则为零 按位或 | 有一则一,否则为零 按位取反 ~ 是零则一,是一为零 按位异或 ^ 不同则一,相同为零 移位 >> << 基本运算: 集合取并 A|B 集合取交 A&B 集合相减 A&~B 集合相减 ALLBITS~A 置位 A

信息学奥赛一本通 5.2 树形动态规划

题解在代码中 二叉苹果树[loj 10153] /* 若要留q条边便是要留q+1个点 所以记忆化搜索 dp[pos][ans]=max(dp[pos][ans],dp[l[pos]][k]+dp[r[pos]][ans-k-1]+a[pos]) 0<=k<=ans-1 */ #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<c