洛谷P1092 虫食算——DFS,优化

本文深入解析洛谷P1092题目,通过递归搜索与优化策略,详细介绍了一种高效的解题方法。文章首先排除了全排列的解题思路,随后逐步介绍了三种递归搜索策略,并最终提出了一种结合即时检查的优化方案,有效提高了算法效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目:https://www.luogu.org/problemnew/show/P1092

思路:

1、如果将n个字符对应的数字,取一个全排列后再检验,肯定会TLE。放弃。

2、为叙述方便,将三个数看作矩阵a,个位为第0列,被加数为第0行,加数为第1行,和数为第2行。

从个位开始,逐列逐行深搜。dfs参数有3个,分别为列号x,行号y,进位数t。当所有行dfs完后检验。这个解法得90分,卡掉一个点。

3、对2进行修改,不再dfs完所有行后检验,而是引入check()函数,若n个字符都已经填好数,则判断是否合法。这个解法较2有改进,dfs层数减少了,但还是卡掉一个点。

4、怎样再优化?以具体例子说明。

  ABC

+DAB

=DDA

深搜试探:C=1,B=2,A=3,D=5,到此,第0、1、2列的字符都确定好,但第2列并不成立。

因此再引入CHECK()函数,从第0列到第n-1列检查,如果某列上的字符都确定了,但不成立,则回溯

bool CHECK(){
	int A,B,C;
	for(int i=0;i<n;i++){
		A=ans[ a[0][i] ];B=ans[ a[1][i] ];C=ans[ a[2][i] ];
		if( A==-1 || B==-1 || C==-1 )continue;
		if( (A+B)%n!=C && (A+B+1)%n!=C ) return 1;
	}
	return 0;
} 

AC代码:

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
int n;
char s[3][30];
int a[3][30],vis[30],ans[30];
int flag=0;

bool CHECK(){
	int A,B,C;
	for(int i=0;i<n;i++){
		A=ans[ a[0][i] ];B=ans[ a[1][i] ];C=ans[ a[2][i] ];
		if( A==-1 || B==-1 || C==-1 )continue;
		if( (A+B)%n!=C && (A+B+1)%n!=C ) return 1;
	}
	return 0;
} 
int check1() {
	for(int i=0;i<n;i++)
		if(vis[i]==0)return 0; 
	return 1;
}
bool check2(){
	int A,B,C,x=0;
	for(int i=0;i<n-1;i++){
		A=ans[ a[0][i] ];B=ans[ a[1][i] ];C=ans[ a[2][i] ];
		if( (A+B+x)%n!=C )return 0;
		x=(A+B+x)/n;
	}
	return 1;
}
void print()
{
    for(int i=0;i<n-1;i++)printf("%d ",ans[i]);
	printf("%d\n",ans[n-1]);
    flag=1;
}

void dfs(int row,int col,int x){/*行,列,进位*/ 
	if(flag)return;
	if( CHECK() )return;
	if(check1()){
		if(check2()){
			print();
			flag=1;
		}
		return;
	}  
	 
    int t=a[row][col];
    if(row!=2){
        if(ans[t]==-1){
            for(int i=n-1;i>=0;i--)
                if(!vis[i]){
                    vis[i]=1;ans[t]=i;
                    dfs(row+1,col,x);
                    vis[i]=0;ans[t]=-1;							
                }
        }
        else dfs(row+1,col,x);
    }
    else{
        int A=ans[ a[0][col] ],B=ans[ a[1][col] ];
        int tmp=A+B+x;
        if(ans[t]==-1){	
            if(!vis[tmp%n]){
            	ans[t]=tmp%n;vis[ans[t]]=1;
                dfs(0,col+1,tmp/n);
                vis[ans[t]]=0;ans[t]=-1;
            }	
        }
        else {
            if(tmp%n==ans[t])dfs(0,col+1,tmp/n);
            else return;
        }
    }	
}

int main(){
    memset(ans,-1,sizeof(ans));
    cin>>n;
    cin>>s[0]>>s[1]>>s[2];
    for(int i=0;i<n;i++){
        a[0][i]=s[0][n-1-i]-'A';a[1][i]=s[1][n-1-i]-'A';a[2][i]=s[2][n-1-i]-'A';
    }
    dfs(0,0,0);
    return 0;
}

总结:用到的3个优化策略,需要仔细体会。

1、数字从大往小尝试;

2、深搜层数最多n层,故所有字符填上数字后就检查整个等式是否成立。

3、前面几列填上数字后,检查到后面是否有某列不成立——这是关键优化。

另:本题还有一种解法,引入4个dfs函数,用于处理一列上有3个、2个、1个、0个字符未知共4种情况,但写起来很考验一个人的功力。以后有时间再给出这种解法。

### 关于洛谷 P3956 DFS 解法 洛谷 P3956 是一道经典的搜索问题,虽然未提供具体题目描述,但从其编号推测可能涉及路径规划、状态转移等问题。以下是基于深度优先搜索(DFS)的通用解法分析。 #### 1. **问题背景** 假设该题目标是在给定的状态空间中寻找符合条件的解决方案。通常情况下,这类问题可以通过穷举所有可能性来解决,而 DFS 正是一种有效的枚举工具[^1]。然而需要注意的是,如果状态空间过大,则可能导致超时(Time Limit Exceeded, TLE)。因此,在实际应用中需优化剪枝策略以提高效率。 #### 2. **法设计** 为了实现高效的 DFS 搜索过程,下面给出一种基本框架: ```cpp #include <bits/stdc++.h> using namespace std; // 定义全局变量或其他必要参数... bool visited[...]; // 记录访问情况 vector<int> path; // 当前路径记录 int result_count = 0; // 符合条件的结果计数器 void dfs(int current_state){ if(达到终止条件){ 更新最优解或者统计数量; return ; } for(auto next : 可能转移到的新状态集合){ if(!visited[next]){ visited[next] = true; path.push_back(next); dfs(next); // 继续探索 path.pop_back(); // 回溯操作 visited[next] = false; // 清除标记以便后续尝试其他分支 } } } int main(){ 初始化数据结构; memset(visited,false,sizeof(visited)); int start_point; cin >> start_point; visited[start_point] = true; path.push_back(start_point); dfs(start_point); 输出最终结果result_count或者其他形式的答案; } ``` 此模板适用于多种场景下的 DFS 实现,只需调整 `current_state` 和 `next` 的定义方式即可适配不同类型的题目需求[^4]。 #### 3. **注意事项** 尽管 DFS 能够很好地处理许多组合型问题,但在面对大规模输入时仍可能存在性能瓶颈。例如当节点数目较多且连接关系复杂时,单纯依靠递归可能会超出内存限制或运行时间限制。此时可考虑引入动态规划(DP)[^1] 或者启发式搜索(A*)等更加高效的方法作为替代方案。 另外值得注意的是,在某些特殊情形下即使采用了合理的剪枝措施也无法完全规避最坏情况的发生概率。所以建议在编码之前仔细阅读样例说明并测试边界状况确保程序健壮性[^5]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值