OJ每日一练试水第4天,聊聊图的问题

时间:2019年5月10日

1.图的基础概念

https://www.cnblogs.com/hslzju/p/5396883.html

图的邻接矩阵的建立

在这里插入图片描述

#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
#include<string.h>

#define MAX 100

typedef struct graph
{
    char vexs[MAX];
    int vexnum;
    int edgnum;
    int matrix[MAX][MAX];
}Graph,*PGraph;

static int get_position(Graph g,char ch)
{
    int i;
    for(i=0;i<g.vexnum;i++)
        if(g.vexs[i]==ch)
            return i;
    return -1;
}

Graph* create_graph()
{
   char vexs[]={'0','1','2','3','4'};//顶点 
   char edges[][2]={{'0','1'},{'0','3'},{'1','2'},{'1','4'},{'2','3'},{'2','4'}};//边的关系 
   int vlen=sizeof(vexs)/sizeof(vexs[0]);//顶点个数 
   int elen=sizeof(edges)/sizeof(edges[0]);//边的个数 
   int i,p1,p2;
   Graph *pG; //在内存中初始化图 
   if((pG=(Graph*)malloc(sizeof(Graph)))==NULL)
        return NULL;
   memset(pG,0,sizeof(Graph));
   
   pG->vexnum=vlen;
   pG->edgnum=elen;
   
   for(i=0;i<pG->vexnum;i++)//将顶点数据传入土地顶点中 
   {
       pG->vexs[i]=vexs[i];
   }
   
   for(i=0;i<pG->edgnum;i++)
   {
   	//将无向图的边的关系,输入字符,然后找到指定的数 
       p1=get_position(*pG,edges[i][0]);
       p2=get_position(*pG,edges[i][1]);
       
       pG->matrix[p1][p2]=1;
       pG->matrix[p2][p1]=1;
   }
   return pG;
}

void print_graph(Graph G)
{
    int i,j;
    printf("matrix Graph:\n");
    for(i=0;i<G.vexnum;i++)
    {
        for(j=0;j<G.vexnum;j++)
            printf("%d ",G.matrix[i][j]);
        printf("\n");
    }
}

int main()
{
    Graph *pG;
    pG=create_graph();
    print_graph(*pG);
}

图的邻接表的建立

在这里插入图片描述

#include <iostream>
#include <stdlib.h>
using namespace std;

typedef struct EdgeNode//边表节点
{
    int adjvex;//存储该顶点对应的下标
    struct EdgeNode *next;//指向该顶点的下一个邻接点
}EdgeNode;

typedef struct VertexNode//顶点表结点
{
    char data;//顶点
    EdgeNode *firstedge;//边表头指针
}VertexNode;

typedef struct//图的邻接表存储结构
{
    VertexNode adjList[5];//有5个VertexNode这种类型的顶点,定义一个数组adjList[5],每个元素是VertexNode类型
    int numVertexes,numEdges;//图中顶点数和边数,这里是5,6
}GraphAdjList;

GraphAdjList *CreateALGraph(GraphAdjList *Gp)//无向图的邻接表创建
{
    Gp=(GraphAdjList *)malloc(sizeof(GraphAdjList));
    //申请一片GraphAdjList大小的类型很重要,否则Gp指向NULL(GL传的值是NULL),程序就运行崩溃了
    EdgeNode *pe;//定义边表指针类型pe
    cout << "input numNodes and numEdges:" << endl;
    cin >> Gp->numVertexes >> Gp->numEdges;//输入5 6
    for (int k = 0 ; k < Gp->numVertexes; k++)
    {
        cout << "input VertexType data:" << endl;
        cin >> Gp->adjList[k].data;//输入A B C D E
        Gp->adjList[k].firstedge = NULL;//将边表头指针指向NULL,即置为0
    }
    for (int k = 0; k <  Gp->numEdges; k++)//建立边表
    {
        int i,j;
        cout << "input vi and vj:" << endl;
        cin >> i >> j;//每次循环依次输入0 1,0 3,1 2,1 4,2 3,2 4

        pe = (EdgeNode *)malloc(sizeof(EdgeNode));
        pe->adjvex = j;// 邻接序号为j
        pe->next = Gp->adjList[i].firstedge;//将pe的指针指向当前顶点指向的结点
        Gp->adjList[i].firstedge = pe;//将当前顶点的指针指向pe

        pe = (EdgeNode *)malloc(sizeof(EdgeNode));
        pe->adjvex = i;
        pe->next = Gp->adjList[j].firstedge;
        Gp->adjList[j].firstedge = pe;//无序图重复上面步骤
    }
    return Gp;
}

int main(void)
{
    GraphAdjList *GL=NULL;
    GL=CreateALGraph(GL);
   
    
     //以下是验证图的创建是否正确
    cout<<"============="<<endl;
    cout<<"查看边表"<<endl;
    cout<<GL->adjList[0].firstedge->adjvex<<endl;
    cout<<GL->adjList[0].firstedge->next->adjvex<<endl;
    cout<<"========"<<endl;
    cout<<GL->adjList[1].firstedge->adjvex<<endl;
    cout<<GL->adjList[1].firstedge->next->adjvex<<endl;
    cout<<GL->adjList[1].firstedge->next->next->adjvex<<endl;
    cout<<"========"<<endl;
    cout<<GL->adjList[2].firstedge->adjvex<<endl;
    cout<<GL->adjList[2].firstedge->next->adjvex<<endl;
    cout<<GL->adjList[2].firstedge->next->next->adjvex<<endl;
    cout<<"========"<<endl;
    cout<<GL->adjList[3].firstedge->adjvex<<endl;
    cout<<GL->adjList[3].firstedge->next->adjvex<<endl;
    cout<<"========"<<endl;
    cout<<GL->adjList[4].firstedge->adjvex<<endl;
    cout<<GL->adjList[4].firstedge->next->adjvex<<endl;
    return 0;
}

在这里插入图片描述

DFS 深度优先算法

【题目1】搭积木(一维dfs)

小明最近喜欢搭数字积木。一共有10块积木,每个积木上有一个数字,0~9。
搭积木规则:
每个积木放到其它两个积木的上面,并且一定比下面的两个积木数字小。
最后搭成4层的金字塔形,必须用完所有的积木。
0
1 2
3 4 5
6 7 8 9 请你计算这样的搭法一共有多少种?

【分析】暴力走到最底层,然后一个个回溯
第一层为0
第二层为1 2
第三层为3 4 5
第四层为6 7 8 9 然后回溯.

0
1 2
3 4 5
6 7 9 8 以此类推

#include<stdio.h>
#include<stdlib.h>


int visited[10]={0};                            //查看该元素是否被访问   全部赋值为0 
int a[10]={0,1,2,3,4,5,6,7,8,9};
int sum=0;                                      //定义全局变量sum来进行对符合条件的组合计数

void ans_printf(int *a); 
int test(int n){                                //判断基本符合条件 
    if(n==2){					//当三个积木 第二层时 
        if(a[0]<a[1]&&a[0]<a[2]){
            return 1;   
        }
        return 0;
    }
    else if(n==5){				//六个积木 第三层时 
        if(a[1]<a[3]&&a[1]<a[4]&&a[2]<a[4]&&a[2]<a[5]){
            return 1;
        }
        return 0;
    }
    else if(n==9){				//10个积木 第四层时 
        if(a[3]<a[6]&&a[3]<a[7]&&a[4]<a[7]&&a[4]<a[8]&&a[5]<a[8]&&a[5]<a[9]){
            sum++;
            ans_printf(a);
            return 1;
        }
        return 0;
    }
    else
        return 1;
}
void ans_printf(int *a){
	printf("======\n");
	for(int i=0;i<10;i++){
		if(i==1){
			printf("\n");
		}else if(i==3){
			printf("\n");
		}else if(i==6){
			printf("\n");
		}
		printf("%d",a[i]);
	}
	printf("\n");
}
/*
void dfs(int n){
    int i,j;
    for(i=0;i<10;i++){
    	if(!visited[i]) //如果该点没有被使用过 
    	{
	    	visited[i]=1; //赋值,表示已经使用 
	       	a[n]=i;   //让当前搜索的积木值为i  a[0]=0 
	       	if(!test(n)) //不满足条件的
			{
				 visited[i]=0;		
	 			 continue;
			} 
			dfs(n+1);//继续往下找     
	  		visited[i]=0;  //回溯过程 
	    }
        
    }
}*/

//注意区分 点 和 点的值 以及 点的使用情况
void dfs(int n)//n表示当前是第几个点    
{
	if(n>9){
		return ;
	} //其实对于这个题目来说,不写没有问题,假设n为10进来,由于前面的点都已经使用完了,所以也不会报错 
	int i,j;
    for(i=0;i<10;i++){ //i表示当前的点可以取的值有10个(0~9) 
    	if(!visited[i]) //如果该点没有被使用过 
    	{
	    	visited[i]=1;  //使用该点
	    	a[n]=i;  //表示当前的结点所使用的值为i
		 	
		 	if(test(n))//判断当前这个点能否过关(满足条件),如果能过关的话 
			{
				dfs(n+1);
				//回溯 
				visited[i]=0;
			} 
			//不能过关的话,就当这个点的值没有被使用过 
			visited[i]=0;
		 	
	    }
    }
} 


int main(){
    dfs(0);
    printf("一共有%d种方法\n",sum);
    return 0;
}

在这里插入图片描述

https://blog.csdn.net/chensanwa/article/details/79717835

【题目2】 走迷宫问题(二维dfs问题)

在给定的一个二维数组中找寻从起点(0,0)开始走,能到达哪些区域(真不记得题意了,大家自行脑补吧)
我们假设上边就是输入数据(嘻嘻,将就一下),‘0’代表能够走的,‘1’代表不能走的,可以理解为墙,同时只能在所给的地图上走,也就是不能跳出去再调回来,求解区域

相信大家对于这个问题其实已经很明确了,其实就和“八连块”问题是一个道理的

【分析】我们从起点开始走,一直沿着一条路走,一直走到最后走不动的时候,进行回溯,再找寻下一条路线,直到最后没有路走,回到了起点位置(大家可以想想为什么会回到起点位置)结束。我们走过的地方其实就是题目的答案
在这里插入图片描述

#include <iostream>
#include <cstring>
#define N 10
using namespace std;
int dir[4][2] = {0,1,0,-1,1,0,-1,0};
int vst[N][N];      //标记数组 
int map[N][N];      //给定图 
bool checkEdge(int x,int y);
void dfs(int x,int y);
int main(){
    int n;
    cin>>n;
    memset(vst,0,sizeof(vst));      //初始化 
    memset(map,-1,sizeof(map));
    for(int i=0;i<n;i++){
        for(int j=0;j<n;j++){
            cin>>map[i][j];     //输入图 
        }
    }
    dfs(0,0);
     for(int i=0;i<n;i++){
        for(int j=0;j<n;j++){
            if(vst[i][j] == 6){     //找寻到的结果, 
                cout<<"i:"<<i<<" j:"<<j<<endl;  
            } 
        }
        cout<<endl;
    }
    return 0;
}
bool checkEdge(int x,int y)
{
	//如果没有访问过,且不为墙,且没有超过数组的边界 
	if(vst[x][y]==0 && map[x][y]==0 && x>=0 && x<5 && y>=0 && y<5){
        return true;
    }
    return false;
 
}
void dfs(int x,int y)
{
	vst[x][y]=6;
	int ax,ay;//表示移动量 
	for(int i=0;i<4;i++)//终止是以所有点的上下左右全部都被访问过了才结束! 
	{
		ax=dir[i][0];//x方向上的移动 
		ay=dir[i][1];//y方向上的移动 
		if(checkEdge(x+ax,y+ay))//检测下一步
		{
			dfs(x+ax,y+ay);
		}
	}
	return ;
}

【题目3】求图中连通子图的个数

【分析】先用一个结点进行dfs,未被遍历的点说明为连通子图,然后 计数器++ 再以连通子图中的一个结点再次dfs
在计数器 ++ 这么依次类推

图的遍历与存储结构有关

BFS 广度优先算法

【题目4】求图的迷宫问题(二维BFS问题 搜索最短路径)

洪尼玛今天准备去寻宝,在一个n*n (n行, n列)的迷宫中,存在着一个入口、一些墙壁以及一个宝藏。由于迷宫是四连通的,即在迷宫中的一个位置,只能走到与它直接相邻的其他四个位置(上、下、左、右)。现洪尼玛在迷宫的入口处,问他最少需要走几步才能拿到宝藏?若永远无法拿到宝藏,则输出-1。

输入
5
S.#…
#.#.#
#.#.#
#…E
#…
输出
7

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<queue>
using namespace std;
char a[1010][1010];//记录整个图形的
int vis[1010][1010];//用来标记的走没走过
struct Point{
     int x, y;
}tmp1, tmp2;
int mt[4][2] = {1,0,-1,0,0,1,0,-1};//移动的四个方向 右 左 下 上 
queue<Point>q;
int main(){
    int n, i, j;
     while(~scanf("%d", &n)){  //成功输入返回一个非0项    ~scanf("%d", &n)  返回值为-2  1的二进制形式为0001 取反为1110 其中1为符号位    110为补码对应的原码为010!!所以为-2  
         for(i = 0; i < n; i++){
            scanf("%s", a[i]);
         }
         int sx, sy;
         int ex, ey;
         for(i = 0; i < n; i++){
            for(j = 0; j < n; j++){
                vis[i][j] = 0;//初始化标记
                if(a[i][j] == 'S'){//找到初始位置
                    sx = i;
                    sy = j;
                }
                else if(a[i][j] == 'E'){//终点位置
                    ex = i;
                    ey = j;
                }
            }
         }
         while(!q.empty()){ //队列初始化 
         	q.pop();
         }
         
         tmp1.x=sx;
		 tmp1.y=sy;     //记录出发位置 
		 vis[tmp1.x][tmp1.y]=1;   //表明已经经过 
		 q.push(tmp1);   //入队 
		 int flag=0;    //标志位(标志有没有解) 
		 
		 while(!q.empty()){ 
         	tmp1 = q.front();   //取出队首元素 
         	q.pop();     //出队 
         	for(int i=0;i<4;i++){
	         	tmp2.x = tmp1.x+mt[i][0];
	         	tmp2.y = tmp1.y+mt[i][1];
	         	if(tmp2.x >=0 && tmp2.x < n && tmp2.y >= 0 && tmp2.y < n && !vis[tmp2.x][tmp2.y] && a[tmp2.x][tmp2.y] != '#')//判断条件 
	         	{
	         		vis[tmp2.x][tmp2.y]=vis[tmp1.x][tmp1.y]+1;   //满足条件 则步数往前进1 
	         		q.push(tmp2);    //入队 
	         	} 
	         }
	         
	         if(vis[ex][ey]){//如果到达终点就标记一下,表示能到达终点
                flag = 1;
                break;
            }
            
         }
         if(flag)printf("%d\n", vis[ex][ey]-1);//终点位置也包括在内,所以减1
         else printf("-1\n");
		  
     }
		 
	return 0;
}

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

黑瞳丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值