状态压缩dp例题

例1 牧场的安排

这道可以判断出这是道状压dp题,但这和普通的状压dp唯一不同的是要用三进制来表示,写起来比普通的二进制有些麻烦

先预处理出每行的所有可能然后每行进行dp

dp[i][j]表示第i行状态为j的方案数

要注意对于给定的那个行要独立进行dp因为那一行的状态已经确定

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<queue>
#define ll long long
using namespace std;
const int maxn=1000000+10101;
const int MOD=1000000;
inline int read(){
    int x=0,f=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
    for(;isdigit(ch);ch=getchar())x=(x<<3)+(x<<1)+ch-'0';
    return x*f;
}

inline void write(ll n){
    char ch[24]; int top=0;
    if(n==0){putchar('0');return ;}
    if(n<0)putchar('-'),n=-n;
    while(n)ch[++top]=n%10+'0',n/=10;
    while(top)putchar(ch[top--]);
    return ;
}

int n,m,k,num=1,tot,s[maxn],ban;
int dp[10005][300];
inline bool init(int x){
    int tmp=0x3f;
    for(int i=1;i<=m;i++){
        if(tmp==x%3)return 0;
        tmp=x%3;x/=3;
    }
    return 1;
}

inline bool check(int x,int y){
    for(int i=1;i<=m;i++){
        if(x%3==y%3)return 0;
        x/=3;y/=3;
    }
    return 1;
}

int main(){
    n=read();m=read();k=read();
    for(int i=1;i<=m;i++)num=(num*3)%MOD;
    for(int i=0;i<num;i++)if(init(i))s[++tot]=i;
    for(int i=1;i<=m;i++){
        int x=read();
        ban=ban*3+x-1;
    }
    int pos=0;
    for(int i=1;i<=tot;i++){
        if(ban==s[i]){pos=i;break;}
    }
    if(!pos){printf("0");return 0;}
    for(int i=1;i<=n;i++){
        if(i==k){
            if(i==1)dp[i][pos]=1;
            else {
                for(int j=1;j<=tot;j++){
                    if(check(s[j],s[pos])){dp[i][pos]+=dp[i-1][j];dp[i][pos]%=MOD;}
                }
            }
        }
        else {
            for(int j=1;j<=tot;j++){
                if(i==1)dp[i][j]=1;
                else for(int K=1;K<=tot;K++){
                    if(check(s[j],s[K])){dp[i][j]+=dp[i-1][K];dp[i][j]%=MOD;}
                }
            }
        }
    }
    int ans=0;
    for(int i=1;i<=tot;i++){
        ans=(ans+dp[n][i])%MOD;    
    }
    write(ans);
    return 0;
}

例2

这道题也是可以看出是状压dp,

还是先预处理每一行的所有状态,

设dp[i][j][k]表示前i行第i行的状态为j,第(i-1)行的状态为k最多能够摆放的炮兵数

以为一行最多有10列,所以最多有2^10的状态(没有考虑可行性),所以dp数组没法开得下,要滚动毕竟影响第i行的只有i-1,i-2行

#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#define ll long long
using namespace std;
const int maxn=1000000+10101;
inline int read(){
    int x=0,f=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
    for(;isdigit(ch);ch=getchar())x=(x<<3)+(x<<1)+ch-'0';
    return x*f;
}

int n,m,s[maxn],dp[3][1025][1026];
int st[maxn],tot,num[maxn];
inline void pre(){
    for(int i=0;i<(1<<m);i++){
        if((i<<1)&i || (i<<2)&i || i&(i>>1) || i&(i>>2))continue;
        st[++tot]=i;
        int k=0;
        for(int j=0;j<m;j++)if(i&(1<<j))k++;
        num[tot]=k;
    }
    return ;
}

inline bool check(int x,int y,int z){
    if(x&y || y&z || x&z || x&y&z)return 0;
    return 1;    
}

int main(){
    n=read();m=read();
    pre();
    for(int i=1;i<=n;i++){
        char a[11];
        scanf("%s",a+1);
        for(int j=1;j<=m;j++){
            if(a[j]=='H')s[i]+=(1<<(m-j));
        }
    }
    for(int i=1;i<=tot;i++){
        if(s[1]&st[i])continue;
        dp[1][i][0]=num[i];    
    }
    for(int i=1;i<=tot;i++){
        if(s[1]&st[i])continue;
        for(int j=1;j<=tot;j++){
            if(st[j]&s[2] || st[i]&st[j])continue;
            dp[2][j][i]=num[j]+num[i];
        }
    }
    for(int i=3;i<=n;i++){
        for(int j=1;j<=tot;j++){
            if(st[j]&s[i])continue;
            for(int k=1;k<=tot;k++){
                if(st[j]&st[k] || st[k]&s[i-1])continue;
                for(int w=1;w<=tot;w++){
                    if(st[w]&s[i-2])continue;
                    if(check(st[k],st[w],st[j])){
                        dp[i%3][j][k]=max(dp[i%3][j][k],dp[(i-1)%3][k][w]+num[j]);    
                    }
                }
            }
        }
    }
    int ans=0;
    for(int i=1;i<=tot;i++){
        for(int j=1;j<=tot;j++){
            ans=max(ans,dp[n%3][i][j]);
        }
    }
    printf("%d",ans);
    return 0;
}

例3:

[APIO2007]动物园

题目太长不截了

这道题一开始没有想法,但仔细读题会发现每个人能看到的围栏个数是定值为5

那么这道题就好做了

f[i][j]表示以i开头的连续五个围栏的状态为j

dp[i][j]表示枚举到第i个围栏且[i,i+5]的围栏移走状态为j时的最多满意人数。则dp[i][j]可以由第i-1个围栏移走和不移走两种状态转移得来

dp[j][s]=max(dp[j-1][(s&15)<<1],dp[j-1][(s&15)<<1|1])+f[j][s];
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#define ll long long
using namespace std;
const int maxn=10000+10101;
inline int read(){
    int x=0,f=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
    for(;isdigit(ch);ch=getchar())x=(x<<3)+(x<<1)+ch-'0';
    return x*f;
}

int n,c,f[maxn][100],dp[maxn][100];
int main(){
    n=read();c=read();
    for(int i=1;i<=c;i++){
        int e,f1,L,l=0,d=0;
        e=read();f1=read();L=read();    
        for(int j=1;j<=f1;j++){
            int x=read();
            x=(x-e+n)%n;
            l+=(1<<x);
        }
        for(int j=1;j<=L;j++){
            int x=read();
            x=(x-e+n)%n;
            d+=(1<<x);
        }
        for(int j=0;j<32;j++){
            if(j&l || ~j&d)f[e][j]++;
        }
    }
    int ans=0;
    for(int i=0;i<32;i++){
        memset(dp[0],128,sizeof(dp[0]));
        dp[0][i]=0;
        for(int j=1;j<=n;j++){
            for(int s=0;s<32;s++){
                dp[j][s]=max(dp[j-1][(s&15)<<1],dp[j-1][(s&15)<<1|1])+f[j][s];
            }
        }
        ans=max(ans,dp[n][i]);
    }
    printf("%d",ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/wzq--boke/p/9863470.html
今日推荐