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