SCAU周训3-H:URAL - 1501

1.题目描述:
在这里插入图片描述
在这里插入图片描述
2.题意:
给你两个01序列,希望你生成一个01序列,使得其在生成过程中,01的差值不超过1。输出可能的选择顺序【任意一个】,或者“Impossible”。

3.思路:
dfs+记忆化。
1)暴力dfs+记忆化路线剪枝。
记录下选择的左右堆以及选择的0和1的个数,碰到重复走的路之间剪枝掉。
部分代码:

int n,m;
char a[maxn],b[maxn];
int tot;
int ans[maxn<<1];
int dp[maxn][maxn];
bool flag;
void dfs(int cnta,int cntb,int one,int zero)
{
	if(cnta==n&&cntb==n) {flag=1;return;}
	if(!dp[cnta][cntb]) dp[cnta][cntb]=1;
	else return;
	if(cnta!=n)
	{
		if(one==zero)
		{
			ans[tot++]=1;
			if(a[cnta]=='0')
			{
				if(dfs(cnta+1,cntb,one,zero+1),flag) return;
			}
			else
			{
				if(dfs(cnta+1,cntb,one+1,zero),flag) return;
			}
			tot--;
		}
		else if(one<zero&&a[cnta]=='1')
		{
			ans[tot++]=1;
			if(dfs(cnta+1,cntb,one+1,zero),flag) return;
			tot--;
		}
		else if(zero<one&&a[cnta]=='0')
		{
			ans[tot++]=1;
			if(dfs(cnta+1,cntb,one,zero+1),flag) return;
			tot--;
		}
	}
	if(cntb!=n)
	{
		if(one==zero)
		{
			ans[tot++]=2;
			if(b[cntb]=='0')
			{
				if(dfs(cnta,cntb+1,one,zero+1),flag) return;
			}
			else
			{
				if(dfs(cnta,cntb+1,one+1,zero),flag) return;
			}
			tot--;
		}
		else if(one<zero&&b[cntb]=='1')
		{
			ans[tot++]=2;
			if(dfs(cnta,cntb+1,one+1,zero),flag) return;
			tot--;
		}
		else if(zero<one&&b[cntb]=='0')
		{
			ans[tot++]=2;
			if(dfs(cnta,cntb+1,one,zero+1),flag) return;
			tot--;
		}
	}
}
li void solve()
{
	scanf("%s",a);
	scanf("%s",b);
	tot=0;flag=0;
	dfs(0,0,0,0);
	if(!flag) puts("Impossible");
	else {rep(i,0,tot) printf("%d",ans[i]);puts("");}
}
int main()
{
	//FAST;
	//srand(time(0));
	//freopen("C:\\Users\\Administrator\\Desktop\\in.txt","r",stdin);
	//for(int QwQ=read();QwQ;QwQ--) solve();
	while(~scanf("%d",&n)) solve();
	return 0;
}

2)合并操作+记忆化路线剪枝。
我们可以分类讨论一下可能的情况:
a)如果一堆的最上面两张是相异的,我们就可以直接拿走了。
b)如果两堆的最上面一张是相异的,我们也可以直接拿走了。
递归重复这两种情况,然后分别在路径上输出结果就OK了。

部分代码:

int n,m;
char a[maxn],b[maxn];
int dp[maxn][maxn];
int dfs(int x,int y)
{
	if(!x&&!y) return true;
	if(!dp[x][y]) dp[x][y]=true;
	else return false;
	if(x>=2&&a[x]!=a[x-1]&&dfs(x-2,y)){printf("11");return true;}
	if(y>=2&&b[y]!=b[y-1]&&dfs(x,y-2)){printf("22");return true;}
	if(x&&y&&a[x]!=b[y]&&dfs(x-1,y-1)){printf("12");return true;}
	return false;
}
li void solve()
{
	scanf("%s%s",a+1,b+1);
	if(!dfs(n,n)) puts("Impossible");
	else puts("");
}
int main()
{
	//FAST;
	//srand(time(0));
	//freopen("C:\\Users\\Administrator\\Desktop\\in.txt","r",stdin);
	//for(int QwQ=read();QwQ;QwQ--) solve();
	while(~scanf("%d",&n)) solve();
	return 0;
}

3)dfs+dp剪枝
这个是存储状态来剪枝的。我们把前面一个状态保留下来,用于判断现在这个状态如何。
dp[i][j]就意味着,前面一堆选i个,后面一堆选j个。
dp的值:
-2:代表这个状态不能得到正确的解。
-1:初始值
0:上一状态0和1的数量相同。
1:0比1多
2:1比0多
部分代码:

int n,m;
char a[maxn],b[maxn];
int ans[maxn<<1];
int dp[maxn][maxn];//-2:失败,0:一样多,1:0多,2:1多 
int dfs(int x,int y)
{
	if(~dp[x][y]) return dp[x][y];
	if(!x&&!y) return dp[x][y]=0;
	if(x)
	{
		if(dfs(x-1,y)==0)
		{
			ans[x+y]=1;
			if(a[x]=='0') return dp[x][y]=1;
			else return dp[x][y]=2;
		}
		if(dfs(x-1,y)==1&&a[x]=='1'){ans[x+y]=1;return dp[x][y]=0;}
		if(dfs(x-1,y)==2&&a[x]=='0'){ans[x+y]=1;return dp[x][y]=0;}
	}
	if(y)
	{
		if(dfs(x,y-1)==0)
		{
			ans[x+y]=2;
			if(b[y]=='0') return dp[x][y]=1;
			else return dp[x][y]=2;
		}
		if(dfs(x,y-1)==1&&b[y]=='1'){ans[x+y]=2;return dp[x][y]=0;}
		if(dfs(x,y-1)==2&&b[y]=='0'){ans[x+y]=2;return dp[x][y]=0;}
	}
	return dp[x][y]=-2;
}
li void solve()
{
	mem(dp,-1);
	mem(ans,0);
	scanf("%s",a+1);
	scanf("%s",b+1);
	if(dfs(n,n)==-2) puts("Impossible");
	else {rep(i,1,(n<<1)+1) printf("%d",ans[i]);puts("");}
}
int main()
{
	//FAST;
	//srand(time(0));
	//freopen("C:\\Users\\Administrator\\Desktop\\in.txt","r",stdin);
	//for(int QwQ=read();QwQ;QwQ--) solve();
	while(~scanf("%d",&n)) solve();
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值