ACM Weekly 7(待修改)

涉及的知识点

第六周的练习主要涉及深度优先搜索DFS、广度优先搜索BFS、记忆化搜索

拓展:深搜剪枝、SPFA、基环树、负环判断、差分约束

DFS

深度优先搜索,是常用的状态遍历的方法,通俗来讲,其核心思想便是一条路走到头,遍历所有状态,求出所有可行解

代码实现

void DFS(int& i)
{
	if(i>=N)//到达边界就返回
		return ;
	for(int j=1;j<=N;j++)
		if(!visited[j])
		{
			visited[j]=true;//在DFS前可以增加操作
			DFS(j);
			visited[j]=false;//还原使用,有时也不需要
		}
}

例题

在这里插入图片描述
题目大意:给出三个字符串A、B、C,判断A与B在不变更相对顺序的情况下能否合成C

思路:直接DFS,从A、B中抽取和C对应位置字符相等的元素进行判断,A不行再判断B,B不行说明不能合成

代码

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cstdlib>
using namespace std;
int N;
char A[1000],B[1000],C[1000];//三个字符串
int Type[1212][1212];
int DFS(int a,int b,int c)//核心部分
{
    if(Type[a][b])//如果这一状态已经遍历过,返回该状态
        return Type[a][b];
    if(C[c]=='\0')//如果匹配到最后一个字符,返回1
        return 1;
    Type[a][b]=2;//设置状态为2,假设不匹配
    if(C[c]==A[a])//如果A和C匹配,则匹配A与B的下一位
        Type[a][b]=DFS(a+1,b,c+1);
    if(C[c]==B[b]&&Type[a][b]!=1)//如果A匹配失败,并且B与C匹配,则匹配B与C的下一位
        Type[a][b]=DFS(a,b+1,c+1);
    return Type[a][b];
}
int main()
{
    cin >>N;
    getchar();
    for(int i=1; i<=N; i++)
    {
        scanf("%s",A);
        getchar();
        scanf("%s",B);
        getchar();
        scanf("%s",C);
        getchar();
        printf("Data set %d: ",i);
        if(DFS(0,0,0)==1)
            printf("yes");
        else
            printf("no");
            putchar('\n');
        memset(Type,0,sizeof(Type));
    }
    return 0;
}

BFS

广度优先搜索BFS,与DFS重要性平分秋色,通俗讲便是层次遍历,将首个点点周边所有点进行拓展,作为一层,删除该点,以此类推,求出最优解

代码实现

typedef pair<int,int>PR;
int Next[4][2]={0,1,0,-1,1,0,-1,0};
void BFS(PR root)
{
	queue<PR>Q;
	Q.push(root);
	while(!Q.empty())
	{
		PR t=Q.front();
		Q.pop();
		if(visited[t.first][t.second])
			continue;
		visited[t.first][t.second]=true;
		for(int i=0;i<4;i++)
		{
			int x=t.first,y=t.second;
			x+=Next[i][0];
			y+=Next[i][1];
			if(x>=1&&x<=N&&y>=1&&y<=M&&!visited[x][y]&&!Graph[x][y])
				Q.push(make_pair(x,y));
		}
	}
}

例题

在这里插入图片描述
题目大意:统计连通域的个数

思路:直接BFS,数据规模小,并且到了后面还会越来越快,可以直接暴力

代码

#include <iostream>
#include <queue>
#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace std;
typedef pair<int,int>PR;
int K,Next[4][2]= {0,1,1,0,0,-1,-1,0},M,N;
bool Graph[120][120],visited[120][120];
void BFS(int i,int j)
{
    queue<PR>Q;
    Q.push(make_pair(i,j));
    while(!Q.empty())
    {
        PR t=Q.front();
        Q.pop();
        if(visited[t.first][t.second])
            continue;
        visited[t.first][t.second]=true;
        for(int i=0; i<4; i++)
        {
            int x=t.first,y=t.second;
            x+=Next[i][0];
            y+=Next[i][1];
            if(x>=1&&x<=N&&y>=1&&y<=M&&!visited[x][y]&&Graph[x][y])
                Q.push(make_pair(x,y));
        }
    }
}
int main()
{
    //freopen("test.txt","r",stdin);
    cin >>K;
    while(K--)
    {
        int ans=0;
        cin >>N>>M;
        for(int i=1; i<=N; i++)
            for(int j=1; j<=M; j++)
            {
                char ch;
                cin >>ch;
                if(ch=='#')
                    Graph[i][j]=true;
            }
        for(int i=1; i<=N; i++)
            for(int j=1; j<=M; j++)
                if(!visited[i][j]&&Graph[i][j])
                {
                    BFS(i,j);
                    ans++;
                }
        cout <<ans<<endl;
        memset(Graph,0,sizeof(Graph));
        memset(visited,0,sizeof(visited));
    }
    return 0;
}

记忆化搜索

记忆化搜索即搜索+记录上一层已经计算的结果,思想是搜索+动态规划,记录已经算出的结果,可以省略大量重复的步骤,从而提高效率

由于记忆化搜索总结出通用性代码不太现实,所以直接采用例题分析

例题1

在这里插入图片描述
题目大意:给出一种递归过程,要求算出输入参数时的值

思路:直接模拟+记录已算出数值,由给出的递归思路可知,可以先预处理w(20,20,20)

代码

#include <iostream>
#include <cstdio>
#include <cstdlib>
using namespace std;
typedef long long ll;
ll value[100][100][100]= {1};
ll GetValue(ll a,ll b,ll c)
{
    if(value[a][b][c])
        return value[a][b][c];
    if(a<=0||b<=0||c<=0)
        return 1;
    else if(a>20||b>20||c>20)
        return value[a][b][c]=1048576;
    else if(a<b&&b<c)
        return value[a][b][c]=GetValue(a,b,c-1)+GetValue(a,b-1,c-1)-GetValue(a,b-1,c);
    else
        return value[a][b][c]=GetValue(a-1,b,c)+GetValue(a-1,b-1,c)+GetValue(a-1,b,c-1)-GetValue(a-1,b-1,c-1);

}
int main()
{
    ll a,b,c;
    value[20][20][20]=1048576;
    while(scanf("%lld%lld%lld",&a,&b,&c)!=EOF)
    {
        if(a==-1&&b==-1&&c==-1)
            break;
        printf("w(%lld, %lld, %lld) = ",a,b,c);
        if(a<=0||b<=0||c<=0)
            printf("1");
        else
            printf("%lld",GetValue(a,b,c));
        putchar('\n');
    }
    return 0;
}

例题2

在这里插入图片描述
在这里插入图片描述
题目大意:

思路:

代码

难题解析

例题1

拓展

深搜剪枝

  1. 最优化剪枝
  2. 估价函数剪枝
  3. 双向搜索
  4. 较优解
  5. 阈值剪枝
  6. 随机(搜索数据或顺序)
    当搜索答案的变化方向远离正确答案时,去掉之后的未搜索的状态

参考文献

  1. 《啊哈算法》
  2. 《挑战程序设计竞赛》
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值