2087
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<stack>
#include<string>
#include<cstring>
#include<map>
#include<iomanip>
const int MAX=1001;
char x[MAX];
char y[MAX];
using namespace std;
int main()
{
int n,m,i,j,sum;
char *s;
while(cin>>x)
{
if(x[0]=='#')
break;
cin>>y;
m=strlen(y);
if(strstr(x,y))//strstr()函数如果在x字符串中找到了y字符串就返回找到y串第一个字符在x中的位置,如果找不到返回空即(NULL)
{
sum=0;
s=x;//把x字符串首地址给指针s
while(s=strstr(s,y))//找y串,直到返回NULL
{
sum+=1;//找到了就+1
s+=m;//并且指针向后m个地址,因为y串的长度就是m
}
cout<<sum<<endl;
}
else
cout<<0<<endl;
}
}
2089
#include<iostream>
#include<cstring>
using namespace std;
bool shu[1000010];
void jishu()
{
int i;
for(i=1;i<=1000010;i++)
{
for(int j=i;j>0;j/=10)
if(j%100==62||j%10==4)
{
shu[i]=false;break;
}
else shu[i]=true;
}
}
int main()
{
int a,b;
jishu();
while(scanf("%d%d",&a,&b)!=EOF)
{
int i,k=0;
if(a==0&&b==0) break;
for(i=a;i<=b;i++)
{
if(shu[i])
k++;
}
printf("%d\n",k);
}
return 0;
}
搜索
引入 (图的遍历)
从图的任意指定顶点出发,依照某种规则去访问图中所有顶点,且每个顶点仅被访问一次,这一过程叫做图的遍历。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-koydKfx1-1612709407428)(C:\Users\ADMINI~1\AppData\Local\Temp\1612006121967.png)]
图的遍历按照深度优先和广度优先规则去实施,通常有深度优先遍历法(Depth_First Search——DFS )和 广度优先遍历法( Breadth_Frist Search——BFS)两种。
深度优先遍历
方法:1、访问指定的起始顶点;
2、若当前访问的顶点的邻接顶点有未被访问的(通过标记数组实现),则任选一个访问;反之,退回到 最近访问过的顶点;直到与起始顶点相通的全部顶点都访问完毕;
3、若此时图中尚有顶点未被访问,则再选其中一个顶点作为起始顶点并访问之,转 2; 反之,遍历结束。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XFADOZiP-1612709407431)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210131084211317.png)]
一条道走到黑,走不完再倒回去
广度优先遍历
方法:从图的某一结点出发,首先依次访问该结点的所有邻 接顶点 V1, V2, …, Vn 再按这些顶点被访问的先后次序依次访问 与它们相邻接的所有未被访问的顶点,重复此过程,直至所有顶点均被访问为止。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-os4eDzGN-1612709407432)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210131085125400.png)]
一层一层的走
深度搜索 (DFS)
算法过程
状态A(我是谁,我在哪)
- 判断当前状态是否满足题目需要,满足则进行保存,比较,输出等操作
- 判断当前状态是否合法(当前状态是否满足题目要求,或者数组是否越界),满足继续执行否则回到上次调用
- 往下走一层,递归调用dfs()
例题一:
求出n的全排列
1:全排列函数
//全排列的函数:next_permutation(start,end),和prev_permutation(start,end)。这两个函数作用是一样的,区别就在于前者求的是当前排列的下一个排列,后一个求的是当前排列的上一个排列。
//当前序列不存在下一个排列时,函数返回false,否则返回true
#include <iostream>
#include <algorithm>//用此头文件
using namespace std;
int arr[1000];
int main(void)
{
int n;
cin >> n;
for (int i = 0; i < n; i++)arr[i] = i + 1;
do{//采用do-while循环,用来输出初始的序列
for (int i = 0; i < n; i++) cout << arr[i] << ' ';
cout << endl;
}while (next_permutation(arr, arr + n));
return 0;
}
2:深搜
#include <iostream>
#include <algorithm>
using namespace std;
int arr[1000];
int book[1000];
int n;
void dfs(int step)//step表示当前处于第step个盒子
{
if (step == n + 1)//如果当前处于第n+1个盒子,说明n个盒子已经处理完成,输出序列
{
for (int i = 1; i <= n; i++)cout << arr[i] << ' ';
cout << endl;
return;//返回到第n个盒子继续处理,特别注意,否则卡死
}
for (int i = 1; i <= n; i++)
{//每次遍历1-n的数字,如果此数字之前没有被用过(book[i]=0),则可以填到盒子里面
if (!book[i])
{//核心操作(其中1,2,4需要根据实际情况变化)
// 1:首先将遍历过的进行标记
// 2:将当前状态赋值
// 3:递归搜索
// 4:回到当前状态以后,之前的标记要消除
book[i] = 1;
arr[step] = i;
dfs(step + 1);
book[i] = 0;
}
}
}
int main(void)
{
cin >> n;
dfs(1);
return 0;
}
例题二
整数划分(搜索剪枝:去除一些显然不可能的情况,减少搜索次数)
一个正整数可以划分为多个正整数的和,比如n=6时: 6;1+5;2+4;3+3;2+2+2;1+1+4;1+2+3;1+1+1+3;1+1+2+2;1+1+1+1+2;1+1+1+1+1+1 共有十一种划分方法。 给出一个正整数,问有多少种划分方法。
基本思路:正整数n可以看成n个小球,每一个小球代表数字一,观察可知划分即为将这n个小球放入k的盒子里面,且每一个盒子不能为空,则k依次为从1到n,k=n时即为n个1相加。每次搜索k个盒子
//最基本的朴素搜索(必不能AC)
#include <iostream>
#include <cstdio>
using namespace std;
int arr[1000];
int n;
int sum;
void dfs(int step,int k)//step表示当前处于第step个盒子
{
if (step == k + 1)
{
int num = 0;
for (int i = 1; i <= n; i++)
num += arr[i];
if (num == n)
{
printf("%d=%d",n,arr[1]);
for (int i = 2; i <= k; i++)
printf("+%d", arr[i]);
cout << endl;
sum++;
}
return;
}
for (int i = 1; i <= n; i++)
{
arr[step] = i;//数字可以重复使用无需标记
if (arr[step] >= arr[step - 1])
{//因为1 2 3和3 2 1是同一种情况,所以让后一个数小于等于前一个数防止重复
dfs(step + 1, k);
}
}
}
int main(void)
{
cin >> n;
for(int i=1;i<=n;i++)
dfs(1,i);
cout << sum;
return 0;
}
//剪枝算法
#include <iostream>
#include <cstdio>
using namespace std;
int arr[1000];
int n;
int sum;
void dfs(int step, int k, int m)//m表示剩余数字的和,即还没有填的数的和
{
if (step == k+1)
{ //如果step==k+1,并且sum=0,表明全部盒子填完且符合情况,输出
if (m == 0)
{
printf("%d=%d", n, arr[1]);
for (int i = 2; i <= k; i++)
printf("+%d", arr[i]);
cout << endl;
sum++;
}
return;
}
for (int i = arr[step - 1]; i <= m / (k - step + 1); i++)//上下界剪枝
{
arr[step] = i;
//保证前面的数字小于后面的数字,每次从step-1开始枚举,且保证枚举的数字不能超过后面总和的平均数
//例如1 2 3 4,为一组解,那么在枚举arr[3]的时候,此时sum = 7,sum/(k-step+1)=3.5,则arr[3]不能超过3,否则arr[4]必定比arr[3]要小
//不满足前面的数字比后面的数字大,造成重复计算
dfs(step + 1, k, m-i);//尝试arr[step]=i后,sum-i;
}
}
int main(void)
{
cin >> n;
arr[0] = 1;//因为有step-1的情况,就把arr[0]设为1
for (int i = 1; i <= n; i++)
{
dfs(1, i, n);
}
cout << sum;
return 0;
}
例题三
迷宫问题
定义一个n行n列的二维数组
例如当n等于5时有
0 1 0 0 0
0 1 0 1 0
0 0 0 0 0
0 1 1 1 0
0 0 0 1 0
它表示一个迷宫,其中的1表示墙壁,0表示可以走的路,只能横着走或竖着走,不能斜着走,要求编程序找出从左上角到右下角的最短路线。
基本思路:从起点出发,每次按照顺序遍历四个方向尝试所有情况,如果合法就进入下一层
#include <iostream>
#include <cstdio>
using namespace std;
typedef long long ll;
ll n, m, min = 0x3fffffff;
ll arr[501][501], book[501][501];//book为标记数组
ll dx[] = {0,1,0,-1};//x方向上的
ll dy[] = {1,0,-1,0};//y方向上的
void dfs(int x, int y, ll step)//x,y表示当前所在的点的横纵坐标,step表示当前的步数
{
ll tx, ty, k;
if (x == n - 1 && y == n - 1)//如果当前所在位置是终点,说明已经走完
{
if (step < min)//如果比最短路径还要小,更新min
min = step;
return;//一定要return
}
for (k = 0; k < 4; k++)
{
tx = x + dx[k];//利用此方法,算出下一个需要走的点的坐标
ty = y + dy[k];
if (tx<0 || tx>n-1 || ty<0 || ty>n-1)//判断边界,如果越界就不执行
continue;
if (book[tx][ty] == 0 && arr[tx][ty] == 0)
{
book[tx][ty] = 1;
dfs(tx, ty, step + 1);//搜素下个点
book[tx][ty] = 0;
}
}
}
int main(void)
{
ll i, j;
scanf("%lld", &n);
for (i = 0; i < n; i++)
{
for (j = 0; j < n; j++)
scanf("%lld", &arr[i][j]);
}
book[0][0] = 1;//起点首先标记
dfs(0, 0, 0);//从起点开始搜索
printf("%lld\n", min);
return 0;
}
广度搜索
解决迷宫问题,一层一层的解决,依次找出一步可以到达的点,两步,三步,…可以到达的点。将每次走过的点加入队列来进行扩展。
数组形态
#include <iostream>
#include <algorithm>
using namespace std;
int arr[501][501];
int book[501][501];
int dx[] ={ 0,1,0,-1 };//规定四个方向
int dy[]= { 1,0,-1,0 };
struct f {
int x;
int y;
int s;
}map[2500];
int n;
void bfs()
{
int tail = 1, head = 1;//将起点入队
map[tail].x = 0, map[tail].y = 0;
map[tail].s = 0;
book[0][0] = 1;//标记起点
tail++;
while (head < tail)
{
int k = 0;
for (int i = 0; i < 4; i++)//遍历四个方向
{
int nx = map[head].x + dx[i];//计算下一个方向
int ny = map[head].y + dy[i];
if (nx<0 || nx>n - 1 || ny<0 || ny>n - 1)
continue;
if (book[nx][ny] == 0 && arr[nx][ny] == 0)
{
book[nx][ny] = 1;
map[tail].x = nx;//更新队列
map[tail].y = ny;
map[tail].s = map[head].s + 1;//等于父亲步数加一
tail++;
}
if (nx == n-1 && ny == n-1)//如果等于终点
{
k = 1;
cout << map[tail - 1].s;//注意要减一
break;
}
} if (k == 1)
break;
head++;//四个方向探索完毕后,head++模拟出队效果
}
}
int main(void)
{
cin >> n;
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
scanf("%d", &arr[i][j]);
}
bfs();
return 0;
}
stl形态
#include <iostream>
#include <cstdio>
#include <queue>
using namespace std;
typedef long long ll;
ll arr[501][501];
ll book[501][501];
ll dx[] = {0,1,0,-1};//x方向上的
ll dy[] = {1,0,-1,0};//y方向上的
struct f {//定义结构体
int x;//表示横坐标
int y;//纵坐标
int s;//走过的步数
};
queue<f>qu;//定义维护的队列
ll n;
void bfs()
{
struct f a;
a.x = 0;//设置起点位置并加入队列
a.y = 0;
a.s = 0;
qu.push(a);
book[0][0] = 1;//首先将起点标记
while (!qu.empty())//如果队列不为空
{
int k = 0;
struct f b = qu.front();//每次取队首元素
qu.pop();//取出之后就弹出
for (int i = 0; i < 4; i++)
{
struct f c;
c.x = b.x + dx[i];
c.y = b.y + dy[i];
c.s = b.s + 1;
if (c.x<0 || c.x>n - 1 || c.y<0 || c.y>n - 1)
continue;
if (book[c.x][c.y] == 0 && arr[c.x][c.y] == 0)//如果这个点可以走且没有标记过,就加入队列
{
book[c.x][c.y] = 1;
qu.push(c);
if (c.x == n - 1 && c.y == n - 1)//如果是终点就输出
{
k = 1;
cout << c.s;
break;
}
}
}
if (k)break;
}
}
int main(void)
{
ll i, j;
scanf("%lld", &n);
for (i = 0; i < n; i++)
{
for (j = 0; j < n; j++)
scanf("%lld", &arr[i][j]);
}
bfs();
return 0;
}
迷宫问题输出路径
基本思路:结构体增加一个变量记录当前点的父亲点的坐标,从终点开始,递归找到最短路径上的每一个点的坐标并打印
#include <iostream>
#include <algorithm>
using namespace std;
int arr[501][501];
int book[501][501];
int dx[] = { 0,1,0,-1 };//规定四个方向
int dy[] = { 1,0,-1,0 };
struct f {
int x;
int y;
int s;
int f;//记录其父亲的标号
}map[2500];
int n;
void print(int x)//函数功能:打印下标为x的点的坐标
{
if (map[x].f != -1)//如果其父亲不是起点,就执行递归
{
print(map[x].f);
printf("( %d , %d )\n", map[x].x, map[x].y);//打印的坐标
}
}
void bfs()
{
int tail = 1, head = 1;//将起点入队
map[tail].x = 0, map[tail].y = 0;
map[tail].s = 0, map[tail].f = -1;
book[0][0] = 1;//标记起点
tail++;
while (head < tail)
{
int k = 0;
for (int i = 0; i < 4; i++)//遍历四个方向
{
int nx = map[head].x + dx[i];//计算下一个方向
int ny = map[head].y + dy[i];
if (nx<0 || nx>n - 1 || ny<0 || ny>n - 1)
continue;
if (book[nx][ny] == 0 && arr[nx][ny] == 0)
{
book[nx][ny] = 1;
map[tail].x = nx;//更新队列
map[tail].y = ny;
map[tail].s = map[head].s + 1;//等于父亲步数加一
map[tail].f = head;//记录其父亲
tail++;
}
if (nx == n - 1 && ny == n - 1)//如果等于终点
{
k = 1;
cout << map[tail - 1].s<<endl;//注意要减一
print(head);//找到了,就开始打印路径
printf("( 0 , 0 )\n");
break;
}
}
if (k == 1)
break;
head++;//四个方向探索完毕后,head++模拟出队效果
}
}
int main(void)
{
cin >> n;
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
scanf("%d", &arr[i][j]);
}
bfs();
return 0;
}
多源最短路
#include <bits/stdc++.h>
using namespace std;
int a[105][105], n;
int m;
int b[10005];
int main()
{
///输入
cin >> n >> m; ///n个岛屿
for(int i = 1; i <= m; i++)
cin >> b[i];
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= n; j++)
{
cin >> a[i][j];
}
}
for(int k = 1; k <= n; k++) ///k表示用哪个点作为中转
for(int i = 1; i <= n; i++) ///i表示起点
for(int j = 1; j <= n; j++) ///j表示终点
if(a[i][j] > a[i][k] + a[k][j])
a[i][j] = a[i][k] + a[k][j];
int ans = 0;
for(int i = 1; i <= m - 1; i++)
ans += a[b[i]][b[i + 1]];
cout << ans;
单源最短路
#include <bits/stdc++.h>
using namespace std;
int a[505][505];
int dis[505]; ///最短距离
int n, m;
int book[505]; ///0表示白色阵营,1表示红色阵营
const int inf = 0x3f3f3f3f; ///无穷大
void dijkstra()
{
book[1] = 1; ///自己到自己不用求
for(int i = 1; i <= n - 1; i++)
{
int minn = inf;
int u;
///找距离红色阵营最近的点u
for(int j = 1; j <= n; j++)
{
if(book[j] == 0 && dis[j] < minn)
{
minn = dis[j];
u = j;
}
}
book[u] = 1;
for(int j = 1; j <= n; j++)
{
if(book[j] == 0 && dis[j] > dis[u] + a[u][j])
{
dis[j] = dis[u] + a[u][j];
}
}
}
}
int main()
{
cin >> n >> m; ///n个点,m条边
///初始化
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= n; j++)
{
if(i == j)
a[i][j] = 0;
else
a[i][j] = inf;
}
}
///输入边
for(int i = 1; i <= m; i++)
{
int x, y, z;
cin >> x >> y >> z;
if(z < a[x][y])
a[x][y] = z;
}
for(int i = 1; i <= n; i++)
dis[i] = a[1][i];
dijkstra();
if(dis[n] < inf)
cout << dis[n];
else
cout << -1;
return 0;
}