【比赛记录】状态压缩专题测试


A. [CCO2015] 路短最
\(dp[i][S]\) 表示走到 \(i\) 点,经过的点集为 \(S\) 的最长路,用类似于 spfa 的方式转移即可。
复杂度是一个 bfs,具体不太会证。

Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define read(x){\
	char ch;\
	int fu=1;\
	while(!isdigit(ch=getchar()))\
		fu-=(ch=='-')<<1;\
	x=ch&15;\
	while(isdigit(ch=getchar()))\
		x=(x<<1)+(x<<3)+(ch&15);\
	x*=fu;\
}
#define pii pair<int,int>
#define pb push_back
#define mp make_pair
#define fir first
#define sec second
using namespace std;
namespace asbt{
namespace cplx{bool begin;}
const int maxn=(1<<18)+5;
const int inf=0x3f3f3f3f;
int n,m,dp[25][maxn];
vector<pii> e[25];
queue<pii> q;
bitset<maxn> vis[25];
namespace cplx{
	bool end;
	il double usdmem(){return (&begin-&end)/1048576.0;}
}
int main(){
	read(n)read(m);
	for(int i=1,u,v,w;i<=m;i++){
		read(u)read(v)read(w);
		e[u].pb(mp(v,w));
	}
	memset(dp,-0x3f,sizeof dp);
	dp[0][1]=0;
	q.push(mp(0,1));
	vis[0][1]=1;
	while(q.size()){
		int u=q.front().fir,S=q.front().sec;
		q.pop(),vis[u][S]=0;
		for(pii i:e[u]){
			int v=i.fir,w=i.sec;
			if(S>>v&1){
				continue;
			}
			int nS=S|1<<v;
			if(dp[v][nS]<dp[u][S]+w){
				dp[v][nS]=dp[u][S]+w;
				if(!vis[v][nS]){
					q.push(mp(v,nS));
					vis[v][nS]=1;
				}
			}
		}
	}
	int ans=-inf;
	for(int S=0;S<1<<n;S++){
		ans=max(ans,dp[n-1][S]);
	}
	printf("%d",ans);
	return 0;
}
}
int main(){return asbt::main();}

B. [GDOI2014] 拯救莫莉斯
因为 \(n\times m\le50\),并且 \(m\le n\),所以 \(m\) 最大为 \(7\)。于是可以状压。
\(f[i][S1][S2]\) 表示第 \(i-1\) 行建造油库的状态为 \(S1\),第 \(i\) 行的状态为 \(S2\),且第 \(1\)\(i-1\) 行都已合法的最小花费,\(g[i][S1][S2]\) 为相应的最小油库数。转移时枚举上一行、这一行、下一行的状态 \(S1\)\(S2\)\(S3\) 即可。
判断转移条件:
((S1|S2|S3|S2<<1|S2>>1)&uS)==uS
其中 \(uS\) 为全集。
时间复杂度 \(O(n8^m)\)

Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define read(x){\
	char ch;\
	int fu=1;\
	while(!isdigit(ch=getchar()))\
		fu-=(ch=='-')<<1;\
	x=ch&15;\
	while(isdigit(ch=getchar()))\
		x=(x<<1)+(x<<3)+(ch&15);\
	x*=fu;\
}
#define popcnt __builtin_popcount
using namespace std;
namespace asbt{
namespace cplx{bool begin;}
const int maxn=(1<<7)+5;
const int inf=0x3f3f3f3f;
int n,m,a[55][15];
int sum[55][maxn];
int f[55][maxn][maxn];
int g[55][maxn][maxn];
namespace cplx{
	bool end;
	il double usdmem(){return (&begin-&end)/1048576.0;}
}
int main(){
	read(n)read(m);
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			read(a[i][j]);
		}
	}
	for(int i=1;i<=n;i++){
		for(int S=0;S<1<<m;S++){
			for(int j=1;j<=m;j++){
				if(S>>(j-1)&1){
					sum[i][S]+=a[i][j];
				}
			}
		}
	}
	memset(f,0x3f,sizeof f);
	memset(g,0x3f,sizeof g);
	int uS=(1<<m)-1;
	for(int S=0;S<=uS;S++){
		f[1][0][S]=sum[1][S];
		g[1][0][S]=popcnt(S);
	}
	for(int i=1;i<n;i++){
		for(int S1=0;S1<=uS;S1++){
			for(int S2=0;S2<=uS;S2++){
				if(f[i][S1][S2]>=inf){
					continue;
				}
				for(int S3=0;S3<=uS;S3++){
					if(((S1|S2|S3|S2<<1|S2>>1)&uS)==uS){
						if(f[i+1][S2][S3]>f[i][S1][S2]+sum[i+1][S3]){
							f[i+1][S2][S3]=f[i][S1][S2]+sum[i+1][S3];
							g[i+1][S2][S3]=g[i][S1][S2]+popcnt(S3);
						}
						else if(f[i+1][S2][S3]==f[i][S1][S2]+sum[i+1][S3]){
							g[i+1][S2][S3]=min(g[i+1][S2][S3],g[i][S1][S2]+popcnt(S3));
						}
					}
				}
			}
		}
	}
	int ans1=inf,ans2=inf;
	for(int S1=0;S1<=uS;S1++){
		for(int S2=0;S2<=uS;S2++){
			if(((S1|S2|S2<<1|S2>>1)&uS)==uS){
				if(ans1>f[n][S1][S2]){
					ans1=f[n][S1][S2];
					ans2=g[n][S1][S2];
				}
				else if(ans1==f[n][S1][S2]){
					ans2=min(ans2,g[n][S1][S2]);
				}
			}
		}
	}
	printf("%d %d",ans2,ans1);
	return 0;
}
}
int main(){return asbt::main();}

C. 萃香抱西瓜
\(dp[t][x][y][S]\) 表示 \(t\) 时刻,坐标为 \((x,y)\),拿到的小西瓜为 \(S\) 的最小移动次数。刷表法转移就行了。
时间复杂度 \(O(hwTn2^m)\)

Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define read(x){\
	char ch;\
	int fu=1;\
	while(!isdigit(ch=getchar()))\
		fu-=(ch=='-')<<1;\
	x=ch&15;\
	while(isdigit(ch=getchar()))\
		x=(x<<1)+(x<<3)+(ch&15);\
	x*=fu;\
}
using namespace std;
namespace asbt{
namespace cplx{bool begin;}
const int inf=0x3f3f3f3f;
const int dx[]={0,0,-1,1};
const int dy[]={-1,1,0,0};
int h,w,tot,sx,sy,n,m,hao[25];
int px[25][105],py[25][105];
int f[105][10][10][(1<<10)+5];
il void upd(int &x,int y){
	x=min(x,y);
}
namespace cplx{
	bool end;
	il double usdmem(){return (&begin-&end)/1048576.0;}
}
int main(){
//	freopen("P3786_2.in","r",stdin);
//	freopen("P3786_2.ans","w",stdout);
	read(h)read(w)read(tot);
	read(sx)read(sy)read(n)read(m);
	m=0;
	for(int i=1,opt,t1,t2;i<=n;i++){
		read(t1)read(t2)read(opt);
		if(opt){
			hao[i]=++m;
		}
		while(t1<t2){
			read(px[i][t1])read(py[i][t1]);
			t1++;
		}
	}
	memset(f,0x3f,sizeof f);
	for(int i=1;i<=n;i++){
		if(px[i][1]==sx&&py[i][1]==sy){
			if(hao[i]){
				f[1][sx][sy][1<<(hao[i]-1)]=0;
			}
			goto togo1;
		}
	}
	f[1][sx][sy][0]=0;
	togo1:;
	for(int t=1;t<tot;t++){
		for(int x=1;x<=w;x++){
			for(int y=1;y<=h;y++){
				for(int S=0;S<1<<m;S++){
					if(f[t][x][y][S]>=inf){
						continue;
					}
					for(int i=1;i<=n;i++){
						if(px[i][t+1]==x&&py[i][t+1]==y){
							if(hao[i]&&(S>>(hao[i]-1)&1)==0){
								upd(f[t+1][x][y][S|1<<(hao[i]-1)],f[t][x][y][S]);
								goto togo2;
							}
							else if(!hao[i]){
								goto togo2;
							}
						}
					}
					upd(f[t+1][x][y][S],f[t][x][y][S]);
					togo2:;
					for(int j=0,nx,ny;j<=3;j++){
						nx=x+dx[j],ny=y+dy[j];
						if(nx<1||nx>w||ny<1||ny>h){
							continue;
						}
						for(int i=1;i<=n;i++){
							if(px[i][t+1]==nx&&py[i][t+1]==ny){
								if(hao[i]&&(S>>(hao[i]-1)&1)==0){
									upd(f[t+1][nx][ny][S|1<<(hao[i]-1)],f[t][x][y][S]+1);
									goto togo3;
								}
								else if(!hao[i]){
									goto togo3;
								}
							}
						}
						upd(f[t+1][nx][ny][S],f[t][x][y][S]+1);
						togo3:;
					}
				}
			}
		}
	}
//	for(int t=1;t<=tot;t++){
//		for(int x=1;x<=w;x++){
//			for(int y=1;y<=w;y++){
//				for(int S=0;S<1<<m;S++){
//					printf("%2d %d %d ",t,x,y);
//					cout<<bitset<10>(S)<<" ";
//					printf("%10d\n",f[t][x][y][S]);
//				}
//			}
//		}
//	}
	int ans=inf;
	for(int x=1;x<=w;x++){
		for(int y=1;y<=h;y++){
			ans=min(ans,f[tot][x][y][(1<<m)-1]);
		}
	}
	printf("%d",ans>=inf?-1:ans);
	return 0;
}
}
int main(){return asbt::main();}

D. [COCI2020-2021#3] Selotejp
我也是打上轮廓线DP了。
\(f[x][y][S]\) 表示当前在 \((x,y)\) 格子,前 \(m\) 个格子的状态为 \(S\) 时的最小花费。
这里的状态是指,这一格竖着覆盖为 \(1\),横着覆盖或本来就不用覆盖为 \(0\)
这里的前 \(m\) 个格子如下图所示,假设 \(m=4\),当前在 \((2,2)\),方格内的数表示在 \(S\) 中从低到高的下标(从 \(0\) 开始):

它就是一个逐行遍历矩阵的顺序,注意是包括 \((x,y)\) 这一格的。
为什么要这样记录呢,因为存在竖着覆盖,如刚才的例子,\((2,2)\) 的下一个为 \((2,3)\),它的上面是 \((1,3)\),刚好被记录了状态。
于是转移其实不难想:

  • 下一位 \((nx,ny)\)#
    • 横着覆盖,判断左边有没有点,且这个点是不是横着覆盖的,即 \(f[nx][ny][S>>1]\)\(f[x][y][S]\)\(f[x][y][S]+1\) 转移。
    • 竖着覆盖,判断上面的点是不是竖着覆盖的,即 \(f[nx][ny][S>>1\mid 1<<(m-1)]\)\(f[x][y][S]\)\(f[x][y][S]+1\) 转移。
  • 下一位为 .,直接让 \(f[nx][ny][S>>1]\)\(f[x][y][S]\) 转移。

复杂度 \(O(nm2^m)\)

Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define read(x){\
	char ch;\
	int fu=1;\
	while(!isdigit(ch=getchar()))\
		fu-=(ch=='-')<<1;\
	x=ch&15;\
	while(isdigit(ch=getchar()))\
		x=(x<<1)+(x<<3)+(ch&15);\
	x*=fu;\
}
using namespace std;
namespace asbt{
namespace cplx{bool begin;}
const int maxn=(1<<10)+5;
const int inf=0x3f3f3f3f;
int n,m,f[maxn][15][maxn];
char s[maxn][15];
il void upd(int &x,int y){
	x=min(x,y);
}
namespace cplx{
	bool end;
	il double usdmem(){return (&begin-&end)/1048576.0;}
}
int main(){
	read(n)read(m);
	for(int i=1;i<=n;i++){
		scanf(" %s",s[i]+1);
	}
	memset(f,0x3f,sizeof f);
	if(s[1][1]=='#'){
		f[1][1][0]=f[1][1][1<<(m-1)]=1;
	}
	else{
		f[1][1][0]=0;
	}
	for(int x=1;x<=n;x++){
		for(int y=1;y<=m;y++){
			for(int S=0,nx,ny;S<1<<m;S++){
				if(f[x][y][S]>=inf){
					continue;
				}
				nx=x+y/m;
				ny=y%m+1;
				if(s[nx][ny]=='#'){
					if(ny>1&&(S>>(m-1)&1)==0&&s[x][y]=='#'){
						upd(f[nx][ny][S>>1],f[x][y][S]);
					}
					else{
						upd(f[nx][ny][S>>1],f[x][y][S]+1);
					}
					if(nx>1&&(S&1)){
						upd(f[nx][ny][S>>1|1<<(m-1)],f[x][y][S]);
					}
					else{
						upd(f[nx][ny][S>>1|1<<(m-1)],f[x][y][S]+1);
					}
				}
				else{
					upd(f[nx][ny][S>>1],f[x][y][S]);
				}
			}
		}
	}
	int ans=inf;
	for(int S=0;S<1<<m;S++){
		ans=min(ans,f[n][m][S]);
	}
	printf("%d",ans);
	return 0;
}
}
int main(){return asbt::main();}
posted @ 2024-12-29 18:07  zhangxy__hp  阅读(6)  评论(0编辑  收藏  举报