状压dp基础习题

状压dp基础习题

常见位运算操作

状压dp基础习题_第1张图片

例题

T1 HDU1565(基础状压dp)

题解

非常基础的一道状压dp
我们用二进制来表示每一行取数情况, 1 1 1表示取, 0 0 0表示不取
很容易得到状态转移方程:
f [ i ] [ j ] = m a x ( f [ i ] [ j ] , f [ i − 1 ] [ k ] + a n s [ i ] [ j ] ) ; f[i][j]=max(f[i][j],f[i-1][k]+ans[i][j]); f[i][j]=max(f[i][j],f[i1][k]+ans[i][j]);当且仅当 j j j& k = = 0 k==0 k==0

代码

#include
#define M 150009
using namespace std;
int read(){
	int f=1,re=0;char ch;
	for(ch=getchar();!isdigit(ch)&&ch!='-';ch=getchar());
	if(ch=='-'){f=-1,ch=getchar();}
	for(;isdigit(ch);ch=getchar()) re=(re<<3)+(re<<1)+ch-'0';
	return re*f;
}
int f[25][M],ans[25][M],a[25][25],cnt,maxn,c[M],n,s[25];
void clear(){//多组数据,注意清空数组
	cnt=maxn=0;
	memset(a,0,sizeof(a));
	memset(ans,0,sizeof(ans));
	memset(f,0,sizeof(f));
}
signed main(){
	while(~scanf("%d",&n)){
		clear();
		for(int i=1;i<=n;i++)
			for(int j=1;j<=n;j++)
				a[i][j]=read();
		for(int i=0;i<(1<<n);i++) if((i&(i>>1))==0) c[++cnt]=i;//存储下每一种合法状态 
		for(int i=1;i<=n;i++){
			for(int j=1;j<=cnt;j++){
				int x=c[j],tot=n;
				while(x){
					if(x&1) ans[i][j]+=a[i][tot];
					tot--,x>>=1;
				}
			}
		}
		for(int i=1;i<=n;i++){
			for(int j=1;j<=cnt;j++){
				for(int k=1;k<=cnt;k++){
					if((c[j]&c[k])!=0) continue;//注意括号 
					f[i][j]=max(f[i][j],f[i-1][k]+ans[i][j]);
					if(i==n) maxn=max(maxn,f[i][j]);
				}
			}
		}printf("%d\n",maxn);
	}return 0;
}

T2 HDU1429(状压bfs)

题解

显然直接bfs是不行的
那么我们用二进制来表示是否具有该钥匙的状态,每到一个地方更新状态即可
代码细节较多

代码

#include
#define M 1509
using namespace std;
int read(){
	int f=1,re=0;char ch;
	for(ch=getchar();!isdigit(ch)&&ch!='-';ch=getchar());
	if(ch=='-'){f=-1,ch=getchar();}
	for(;isdigit(ch);ch=getchar()) re=(re<<3)+(re<<1)+ch-'0';
	return re*f;
}
int dx[4]={-1,1,0,0};
int dy[4]={0,0,-1,1};
int n,m,t,ans;
bool vis[21][21][M];
char mp[49][49],s[40];
struct point{int x,y,step,key;}st;
bool check(int x,int y){
    if(x>=1&&x<=n&&y>=1&&y<=m&&mp[x][y]!='*') return 1;
    return 0;
}
void bfs(){
	queue<point>q;
    memset(vis,0,sizeof(vis));
    vis[st.x][st.y][st.key]=1;
    st.key=st.step=0;
    q.push(st);
    point cur,nxt;
    while(!q.empty()){
        cur=q.front();
        q.pop();
        if(mp[cur.x][cur.y]=='^'){ans=cur.step;return;}
        for(int i=0;i<4;i++){
            nxt.x=cur.x+dx[i];
            nxt.y=cur.y+dy[i];
            nxt.key=cur.key;
            if(check(nxt.x,nxt.y)){
               nxt.step=cur.step+1;
               if(nxt.step>=t) continue;
               else if(mp[nxt.x][nxt.y]>='A'&&mp[nxt.x][nxt.y]<='Z'){
                  int temp=mp[nxt.x][nxt.y]-'A';
                  int nk=cur.key&1<<temp;
                  if(nk&&!vis[nxt.x][nxt.y][nxt.key]){
                      vis[nxt.x][nxt.y][nxt.key]=true;
                      q.push(nxt);
                  }
               }
               else if(mp[nxt.x][nxt.y]>='a'&&mp[nxt.x][nxt.y]<='z'){
                   int temp=mp[nxt.x][nxt.y]-'a';
                   nxt.key=cur.key|1<<temp;
                   if(!vis[nxt.x][nxt.y][nxt.key]){
                       vis[nxt.x][nxt.y][nxt.key]=true;
                       q.push(nxt);
                   }
               }
               else{
                   if(!vis[nxt.x][nxt.y][nxt.key]){
                       vis[nxt.x][nxt.y][nxt.key]=true;
                       q.push(nxt);
                   }
               }
            }
         }
    }ans=-1;return;
}
signed main(){
	while(~scanf("%d%d%d",&n,&m,&t)){
		for(int i=1;i<=n;i++){
			scanf("%s",s+1);
			for(int j=1;j<=m;j++){
				mp[i][j]=s[j];
				if(s[j]=='@') st.x=i,st.y=j;
			}
		}bfs(),printf("%d\n",ans);		
	}return 0;
}

T3 P2704

题解

一道经典的状压dp,细节较多

代码

#include
#define int long long
using namespace std;
int n,m,s[100],ma[109],num[100],cnt,dp[101][101][110];
int read(){
	int f=1,re=0;char ch;
	for(ch=getchar();!isdigit(ch)&&ch!='-';ch=getchar());
	if(ch=='-'){f=-1,ch=getchar();}
	for(;isdigit(ch);ch=getchar()) re=(re<<3)+(re<<1)+ch-'0';
	return re*f;
}
bool pd(int x){
	if(x&(x<<1)) return 0;
	if(x&(x<<2)) return 0;
	return 1;
}
int num_one(int x){
	int sum=0;
	while(x){
		if(x&1) sum++;
		x=x>>1;
	}return sum;
}
void init(){
	for(int i=0;i<=(1<<m)-1;i++)
		if(pd(i)){
			s[cnt]=i;
			num[cnt++]=num_one(i);
		}
}
bool zjf(int x,int y){
	if(x&y) return 0;
	return 1;
}
int solve()
{
	int ans=-100;
	memset(dp,-1,sizeof(dp));
	dp[0][0][0]=0;
	for(int i=0;i<cnt;i++)
		if(zjf(ma[1],s[i])){
			dp[1][i][0]=num[i];
			ans=max(ans,dp[1][i][0]);
		}
	for(int i=2;i<=n;i++)
		for(int j=0;j<cnt;j++)
			if(zjf(ma[i],s[j]))
				for(int k=0;k<cnt;k++)
					if(zjf(ma[i-1],s[k])&&(s[j]&s[k])==0){
						int maxx=0;
						for(int u=0;u<cnt;u++)
							if(dp[i-1][k][u]!=-1&&zjf(ma[i-2],s[u])&&(s[u]&s[j])==0&&(s[u]&s[k])==0)
								maxx=max(maxx,dp[i-1][k][u]);
						dp[i][j][k]=max(maxx+num[j],dp[i][j][k]);
						if(i==n) ans=max(ans,dp[i][j][k]);
					}
	return ans;
}
signed main(){
	char ch;
	n=read(),m=read();
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++){
			cin>>ch;
			if(ch=='H') ma[i]=ma[i]|(1<<(m-j));
		}init();
	cout<<solve()<<endl;
	return 0;
}
}

你可能感兴趣的:(状压dp)