时间:2019年5月10日
每日一练04
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;
}