虫食算

这是一道很好的题目,教给了我很多暴力的剪枝方法

超链接不解释。。。。

https://www.luogu.org/problem/show?pid=1092


解释:

重构了两遍代码才A掉的,当时思路真的有问题,没办法剪枝和处理进位情况,读了一个题解描述。

好了正题

整体思路是从后向前,从上向下的依次搜索三个大数据

几个相当重要且有趣的剪枝+思路:

1.每次搜完就判断是否已经有不合法情况出现:一列三个已知不合法,例如1+1=3;已知两个数未知进位  但 进不进  都不可以满足就不合法;一列已知两个数以及进位计算后不合法。这三种情况直接return

2.重新整理一遍进位

3.在搜一个数时如果所有情况都失败则return;

4.一列已知两个数及进位情况就直接算第三个数 !!且不满足时直接return!!

5.如果不满足3.4,孩子老老实实搜吧,但是从后面往前搜会快哦

#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
bool ff=false,d[30];
int map[4][30],jw[30];
int n,r[30];
bool pd()
{
	int add=0,i;
	for (i=n;i>=1;i--)
	{
		if (r[map[1][i]]!=-1 && r[map[2][i]]!=-1 && r[map[3][i]]!=-1)
	    {
	    	if (add==-1 && (r[map[1][i]]+r[map[2][i]])%n!=r[map[3][i]] && (r[map[1][i]]+r[map[2][i]]+1)%n!=r[map[3][i]])//没有已知进位 
	    	    return false;
	    	if (add!=-1) 
	    	  if ((r[map[1][i]]+r[map[2][i]]+add)%n!=r[map[3][i]]) return false;//有已知进位 
	    	  else add=(r[map[1][i]]+r[map[2][i]]+add)/n;
		}
		else add=-1;
	}
	return true;
}
int find(int i,int j)//有没有知二求三 或者是 直接不合法 
{
	if(j==1)
	{
		if (r[map[2][i]]!=-1 && r[map[3][i]]!=-1)
		  {
		  	int quq=(r[map[3][i]]-r[map[2][i]]+n)%n;
		  	if(jw[i+1]==1) quq=(quq-1+n)%n;
		  	if (d[quq]) return -2;
		  	else return quq;
		  }
		return -1;
	}
	else
	  if (j==2)
	  {
	  	if (r[map[1][i]]!=-1 && r[map[3][i]]!=-1)
		  {
		  	int quq=(r[map[3][i]]-r[map[1][i]]+n)%n;
		  	if(jw[i+1]==1) quq=(quq-1+n)%n;
		  	if (d[quq]) return -2;
		  	else return quq;
		  }
		return -1;
	  }
	else
	  {
	  	if (r[map[1][i]]!=-1 && r[map[2][i]]!=-1)
		  {
		  	int quq=(r[map[2][i]]+r[map[1][i]])%n;
			if(jw[i+1]==1) quq=(quq+1)%n;
		  	if (d[quq]) return -2;
		  	else return quq;
		  }
		return -1;
	  }
}
void gx()//重新整理进位 
{
	int i;
	for (i=n;i>=1;i--)
		if (r[map[1][i]]!=-1 && r[map[2][i]]!=-1 && jw[i+1]!=-1)
		  if (r[map[1][i]]+r[map[2][i]]+jw[i+1]>=n) jw[i]=1; else jw[i]=0;
		else return;	
}
void dfs(int t,int x)
{
	if (ff) return;
	int i,j,k;
	if (!pd()) return;
	gx();
	if (t==0)
	{
		ff=true;
		for (i=1;i<=n-1;i++)
			printf("%d ",r[i]);
		printf("%d ",r[i]);
		return;
	}
	for (k=x;k>=1;k--)
	for (j=1;j<=3;j++)
	  if (r[map[j][k]]==-1)
	{
		int tmp=find(k,j);
		if (tmp==-1)
		{
		for (i=0;i<=n-1;i++)
	     if (!d[i] && !ff)
	     {
	       d[i]=true;
	       r[map[j][k]]=i;
	       dfs(t-1,k);
	       d[i]=false;
	       r[map[j][k]]=-1;
	     }	
	     return;
		}
		else if (tmp==-2) return;
		else
		{
			d[tmp]=true;
			r[map[j][k]]=tmp;
			dfs(t-1,k);
			d[tmp]=false;
			r[map[j][k]]=-1;
			return;
		}
	}
}
int main()
{
    int j;char l;
	scanf("%d\n",&n);
	for (j=1;j<=n;j++)
	{
		scanf("%c",&l);
	    map[1][j]=l-'A'+1;
	} 
	scanf("%d\n");
	for (j=1;j<=n;j++)
	{
		scanf("%c",&l);
	    map[2][j]=l-'A'+1;
	}
	scanf("%d\n");
	for (j=1;j<=n;j++)
	{
		scanf("%c",&l);
	    map[3][j]=l-'A'+1;
	}
	memset(d,0,sizeof(d));
	memset(r,-1,sizeof(r));
	memset(jw,-1,sizeof(jw));
	jw[n+1]=0;
	dfs(n,n);
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值