假期总结(3)

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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值