第六周的练习主要涉及深度优先搜索DFS、广度优先搜索BFS、记忆化搜索
拓展:深搜剪枝、SPFA、基环树、负环判断、差分约束
深度优先搜索,是常用的状态遍历的方法,通俗来讲,其核心思想便是一条路走到头,遍历所有状态,求出所有可行解
代码实现
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
#include
#include
#include
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,与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
#include
#include
#include
#include
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
#include
#include
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