轮廓线dp:GYM103446C

https://vjudge.net/contest/591700#problem/H

考虑轮廓线dp,当我们枚举到蓝色格子的时候,我们记录红色格子的状态

轮廓线dp:GYM103446C_第1张图片

每个格子有4种状态

  1. 0有向下
  2. 1需要向上
  3. 2不用管
  4. 3需向右

每次枚举的时候,我们需要考虑这个格子的三种状态:

  1. 1
  2. 0+不放
  3. 0+放

他们会对所有3和同列的值造成影响

当枚举到行末时,我们需要“换行”,把所有3变成1

发现枚举过程中还有再维护一个0/1状态d,表示此行有没有向左

分类讨论即可

O ( 2 n m 4 m ) O(2nm4^m) O(2nm4m)

#include
using namespace std;
#ifdef LOCAL
 #define debug(...) fprintf(stdout, ##__VA_ARGS__)
#else
 #define debug(...) void(0)
#endif
//#define int long long
inline int read(){int x=0,f=1;char ch=getchar(); while(ch<'0'||
ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f;}
#define Z(x) (x)*(x)
#define pb push_back
#define fi first
#define se second
//srand(time(0));
#define N 11
#define M 100000
//#define mo
void Min(int &a, int b) {
	a=min(a, b); 
}
int n, m, i, j, k, T;
int f[N][N][M][2], s, t, d, ans, c, a[N][N]; 
char str[N]; 

namespace Num {
	int omg, pw[N], b[N]; 
	int Chan1[M], Chan2[M], Find1[M], Get[M][N], becom[M][N][4]; 
	int zh(int *a) {
		int ans=0, i; 
		for(i=1; i<=m; ++i) ans=ans*4+a[i]; 
		return ans; 
	}
	void chai(int s, int *a) {
		int i; 
		for(i=m; i>=1; --i) a[i]=s%4, s/=4; 
	}
	void Pre_num() {
		for(i=1, omg=1, pw[0]=1; i<=m; ++i) omg*=4, pw[i]=pw[i-1]*4; 
		for(i=1; i<=m; ++i) b[i]=2; f[1][0][zh(b)][0]=0; 
		for(s=0; s<omg; ++s) {
			chai(s, b); //all 3 -> 1
			for(i=1; i<=m; ++i) if(b[i]==3) b[i]=1; 
			Chan1[s]=zh(b); 
			
			chai(s, b); //all 3 -> 3
			for(i=1; i<=m; ++i) if(b[i]==3) b[i]=2; 
			Chan2[s]=zh(b); 
			
			chai(s, b); //find s[i]
			for(i=1; i<=m; ++i) Get[s][i]=b[i]; 
			
			for(i=1; i<=m; ++i) if(b[i]==1) break; 
			if(i<=m) Find1[s]=1; //if s has 1
			
			for(i=1; i<=m; ++i) {
				chai(s, b); 
				for(k=0; k<4; ++k) {
					b[i]=k; 
					becom[s][i][k]=zh(b); //make s[i] to k
				}
			}
		}
	}
	int Change(int s, int i, int x) {
		return becom[s][i][x]; 
	}
};


signed main()
{
	#ifdef LOCAL
	  freopen("in.txt", "r", stdin);
	  freopen("out.txt", "w", stdout);
	#endif
//	T=read();
//	while(T--) {
//
//	}
	memset(f, 0x3f, sizeof(f)); 
	n=read(); m=read(); 
	for(i=1; i<=n; ++i) {
		scanf("%s", str+1); 
		for(j=1; j<=m; ++j) a[i][j]=str[j]-'0'; 
	}
	Num::Pre_num(); 
	for(i=1; i<=n; ++i) {
		for(j=1; j<=m; ++j) {
			for(s=0; s<Num::omg; ++s) 
				for(d=0; d<=1; ++d) {
					if(f[i][j-1][s][d]>=100) continue; 
					debug("[%d %d] %d %d\n", i, j-1, s, d); 
					c=Num::Get[s][j]; 
					if(a[i][j]==0 || a[i][j]==2) {
						t=s; 
						if(c==2 && d==0) t=Num::Change(t, j, 3); 
						Min(f[i][j][t][d], f[i][j-1][s][d]); 
						/********************************/
						t=Num::Chan2[s]; 
						t=Num::Change(t, j, 0); 
						Min(f[i][j][t][1], f[i][j-1][s][d]+1); 
					}
					if(a[i][j]==1 || a[i][j]==2) {
						if(c==1) continue; 
						t=Num::Chan1[s]; 					
						if(c==0 || c==2) t=Num::Change(t, j, 2); 
						Min(f[i][j][t][0], f[i][j-1][s][d]);
					}
				}
		}
		for(s=0; s<Num::omg; ++s) for(d=0; d<=1; ++d) {
			if(f[i][m][s][d]>=100) continue; 
			t=Num::Chan1[s]; 
			Min(f[i+1][0][t][0], f[i][m][s][d]); 
		}
	}
	ans=1e9; 
	for(s=0; s<Num::omg; ++s) if(Num::Find1[s]==0) Min(ans, f[n+1][0][s][0]); 
	printf("%d", ans); 
	return 0;
}


你可能感兴趣的:(状压dp,轮廓线dp)