逐渐离谱起来的数据结构作业。。。
独立完成本次作业还是有一些收获的,就是有点费身体,我也是熬夜肝完的,5555~。
写完之后,这次作业还是老样子,让人破防的填代码+应用。前面几道模板题搞懂每个结构体内部的意思一个问题不大。
问题 A: 将邻接矩阵存储的图转换为邻接表存储的图,附加代码模式
code:
#include<iostream>
#include<cstdio>
using namespace std;
#define MAX_SIZE 100
struct Graph{
int vexNumber;
string info[MAX_SIZE];
int adjMatrix[MAX_SIZE][MAX_SIZE];
};
//弧节点定义
struct ArcNode
{
int weight;//弧上的信息部分
int adj;//邻接点的序号
ArcNode *nextarc;
};
//顶点节点定义
struct VexNode
{
string Info;//顶点上的信息部分
ArcNode *firstarc;//弧链头指针
};
//邻接表结构的图的定义
struct linkGraph
{
VexNode *vexes;
int vexnumber;
};
int InitGraph(linkGraph &G,int vexnumber){
G.vexes=new VexNode[vexnumber];
G.vexnumber=vexnumber;
for(int i=0;i<vexnumber;i++){
G.vexes[i].firstarc=NULL;
}
return 0;
}
//将邻接矩阵存储的图转换为邻接表存储的图
void InitGraph(linkGraph &G,const Graph&g){
InitGraph(G,g.vexNumber);
for(int i=0;i<g.vexNumber;i++){
ArcNode *p=G.vexes[i].firstarc;
for(int j=0;j<g.vexNumber;j++){
if(g.adjMatrix[i][j]==1){
ArcNode *t=new ArcNode;
t->adj=j;
// p->nextarc=t;
//p=t;
t->nextarc=G.vexes[i].firstarc;
G.vexes[i].firstarc=t;
// p=G.vexes[i].firstarc;
}
}
}
return;
}
int DestroyGraph(linkGraph &G){
for(int i=0;i<G.vexnumber;i++){
while(G.vexes[i].firstarc!=NULL){
ArcNode *p=G.vexes[i].firstarc;
G.vexes[i].firstarc=p->nextarc;
delete p;
}
}
delete[]G.vexes;
G.vexes=NULL;
G.vexnumber=0;
return 0;
}
void PrintGraph(const linkGraph &G){
for(int i=0;i<G.vexnumber;i++){
printf("%c",i+'a');
ArcNode *p=G.vexes[i].firstarc;
while(p){
printf(" --> %c",p->adj+'a');
p=p->nextarc;
}
printf("\n");
}
}
问题 B: 邻接表存储的图转化为邻接矩阵存储的图-附加代码模式
code:
//根据输入构造邻接表存储的图
void InputlinkGraph(linkGraph &LG){
int n;
cin>>n;
LG.vexes=new VexNode[n];
LG.vexnumber=n;
for(int i=0;i<n;i++){
LG.vexes[i].firstarc=new ArcNode;
LG.vexes[i].firstarc->adj=i;
LG.vexes[i].firstarc->nextarc=NULL;
}
for(int i=0;i<n;i++){
string s;
cin>>s;
ArcNode *t=LG.vexes[i].firstarc;
for(int j=1;j<s.size();j++){
if(s[j]>='a'&&s[j]<='z'){
ArcNode*p=new ArcNode;
p->adj=s[j]-'a';
t->nextarc=p;
t=t->nextarc;
}
}
}
}
//将邻接表存储的图转化为邻接矩阵存储的图
void linkGraph2Graph(const linkGraph&LG,Graph& G){
G.vexNumber=LG.vexnumber;
memset(G.adjMatrix,0,sizeof(G.adjMatrix));
for(int i=0;i<G.vexNumber;i++){
ArcNode *p=LG.vexes[i].firstarc;
while(p->nextarc){
G.adjMatrix[i][p->nextarc->adj]=1;
p=p->nextarc;
}
}
}
void PrintlinkGraph(const linkGraph &G){
for(int i=0;i<G.vexnumber;i++){
printf("%c",i+'a');
ArcNode *p=G.vexes[i].firstarc;
while(p->nextarc){
printf(" --> %c",p->nextarc->adj+'a');
p=p->nextarc;
}
printf("\n");
}
}
//输出邻接矩阵
void printGraph(const Graph& G){
for(int i=0;i<G.vexNumber;i++){
for(int j=0;j<G.vexNumber;j++){
cout<<G.adjMatrix[i][j]<<" ";
}
cout<<endl;
}
}
问题 C: 邻接矩阵存储图的DFS(附加代码模式)
struct Graph
{
int vexNumber;
string vexInfo[MAX_SIZE];
int adjMatrix[MAX_SIZE][MAX_SIZE];
};
//查找V0的未访问的邻接点
int findAdjVex(const Graph&G,int v0,int visited[])
{
for(int i=0;i<G.vexNumber;i++){
if(!visited[i]&&G.adjMatrix[v0][i])return i;
}
return -1;
}
void DFS(const Graph&G,int v0,int visited[]){
cout<<v0<<" ";
visited[v0]=1;
for(int i=0;i<G.vexNumber;i++){
if(G.adjMatrix[v0][i]&&!visited[i])DFS(G,i,visited);
}
}
void DFS(const Graph& G){
int visited[MAX_SIZE];
memset(visited,0,sizeof visited);
for(int i=0;i<G.vexNumber;i++){
if(!visited[i])DFS(G,i,visited);
}
}
问题 D: 邻接矩阵存储图的DFS-非递归算法(附加代码模式)
code:
#include<bits/stdc++.h>
using namespace std;
struct Graph
{
int vexNumber;
string vexInfo[MAX_SIZE];
int adjMatrix[MAX_SIZE][MAX_SIZE];
};
//查找V0的未访问的邻接点
int findAdjVex(const Graph&G,int v0,int visited[])
{
for(int i=0;i<G.vexNumber;i++){
if(!visited[i]&&G.adjMatrix[v0][i])return i;
}
return -1;
}
string DFS(const Graph&G,int v0,int visited[]){
string result="";
string s=to_string(v0);
result+=s;
//cout<<result<<endl;
result.push_back(' ');
visited[v0]=1;
for(int i=0;i<G.vexNumber;i++)
{
if(visited[i]==0&&G.adjMatrix[v0][i]){
result+=DFS(G,i,visited);
}
}
//cout<<result<<endl;
return result;
}
string DFS(const Graph& G){
string result="";
int visited[MAX_SIZE];
memset(visited,0,sizeof visited);
for(int i=0;i<G.vexNumber;i++){
if(!visited[i])result+=DFS(G,i,visited);
}
return result;
}
/*int main(){
Graph G;
cin>>G.vexNumber;
for(int i=0;i<G.vexNumber;i++){
G.vexInfo[i]=to_string(i);
for(int j=0;j<G.vexNumber;j++){
cin>>G.adjMatrix[i][j];
}
}
string str=DFS(G);
cout<<str<<endl;
return 0;
}*/
问题 E: 邻接矩阵存储图的BFS
#include<iostream>
#include<cstdio>
#include<bits/stdc++.h>
using namespace std;
#define MAX_SIZE 100
struct Graph
{
int vexNumber;
string vexInfo[MAX_SIZE];
int adjMatrix[MAX_SIZE][MAX_SIZE];
};
//查找V0的未访问的邻接点
int findAdjVex(const Graph&G,int v0,int visited[])
{
for(int i=0;i<G.vexNumber;i++){
if(!visited[i]&&G.adjMatrix[v0][i])return i;
}
return -1;
}
/*string DFS(const Graph&G,int v0,int visited[]){
string result="";
string s=to_string(v0);
result+=s;
//cout<<result<<endl;
result.push_back(' ');
visited[v0]=1;
for(int i=0;i<G.vexNumber;i++)
{
if(visited[i]==0&&G.adjMatrix[v0][i]){
result+=DFS(G,i,visited);
}
}
//cout<<result<<endl;
return result;
}
string DFS(const Graph& G){
string result="";
int visited[MAX_SIZE];
memset(visited,0,sizeof visited);
for(int i=0;i<G.vexNumber;i++){
if(!visited[i])result+=DFS(G,i,visited);
}
return result;
}*/
int v[MAX_SIZE];
queue<int>q;
void BFS(Graph &G,int v0){
cout<<v0<<" ";
v[v0]=1;
for(int i=0;i<G.vexNumber;i++){
if(!v[i]&&G.adjMatrix[v0][i]){
q.push(i);
v[i]=1;
}
}
while(!q.empty()){
int x=q.front();
q.pop();
BFS(G,x);
}
}
int main(){
Graph G;
cin>>G.vexNumber;
for(int i=0;i<G.vexNumber;i++){
G.vexInfo[i]=to_string(i);
for(int j=0;j<G.vexNumber;j++){
cin>>G.adjMatrix[i][j];
}
}
for(int i=0;i<G.vexNumber;i++){
if(!v[i])BFS(G,i);
}
return 0;
}
问题 F: 案例6-1.2:邻接表存储图的广度优先遍历
题目描述
一个图有n个节点编号从0至n-1和m条边编号从0至m-1。 输出从点x开始的广度优先遍历顺序。
输入格式
第一行为n、m、x。
接下来m行每行有一组u,v。表示点u可以到达点v,点v也可以到达点u。
输出格式
输出经过点的顺序。(输出字典序最小的答案)
输入样例 复制
7 9 5
0 3
0 2
0 4
3 1
3 2
4 5
1 5
2 5
5 6
输出样例 复制
5 1 2 4 6 3 0
思路:把E题改一下就出来了
code:
#include<iostream>
#include<cstdio>
#include<bits/stdc++.h>
using namespace std;
#define MAX_SIZE 100
struct Graph
{
int vexNumber;
string vexInfo[MAX_SIZE];
int adjMatrix[MAX_SIZE][MAX_SIZE];
};
//查找V0的未访问的邻接点
int findAdjVex(const Graph&G,int v0,int visited[])
{
for(int i=0;i<G.vexNumber;i++){
if(!visited[i]&&G.adjMatrix[v0][i])return i;
}
return -1;
}
/*string DFS(const Graph&G,int v0,int visited[]){
string result="";
string s=to_string(v0);
result+=s;
//cout<<result<<endl;
result.push_back(' ');
visited[v0]=1;
for(int i=0;i<G.vexNumber;i++)
{
if(visited[i]==0&&G.adjMatrix[v0][i]){
result+=DFS(G,i,visited);
}
}
//cout<<result<<endl;
return result;
}
string DFS(const Graph& G){
string result="";
int visited[MAX_SIZE];
memset(visited,0,sizeof visited);
for(int i=0;i<G.vexNumber;i++){
if(!visited[i])result+=DFS(G,i,visited);
}
return result;
}*/
int v[MAX_SIZE];
queue<int>q;
void BFS(Graph &G,int v0){
cout<<v0<<" ";
v[v0]=1;
for(int i=0;i<G.vexNumber;i++){
if(!v[i]&&G.adjMatrix[v0][i]){
q.push(i);
v[i]=1;
}
}
while(!q.empty()){
int x=q.front();
q.pop();
BFS(G,x);
}
}
int main(){
Graph G;
int m,n,x;
cin>>m>>n>>x;
if(m==0&&n==0)cout<<0;
while(n--){
int a,b;
cin>>a>>b;
G.adjMatrix[a][b]=G.adjMatrix[b][a]=1;
}
G.vexNumber=m;
for(int i=x;i<G.vexNumber;i++){
if(!v[i])BFS(G,i);
}
for(int i=0;i<x;i++){
if(!v[i])BFS(G,i);
}
return 0;
}
问题 G: 邻接矩阵存储图的DFS完成序求解(附加代码模式)
输入格式
输入的第一行包含一个正整数n,表示图中共有n个顶点。其中n不超过26。
默认每个顶点对应的信息依次为从a到z的小写字母。
以后的n行中每行有n个用空格隔开的整数0或1,对于第i行的第j个0或1,1表示第i个顶点和第j个顶点有直接连接,0表示没有直接连接。当i和j相等的时候,保证对应的整数为0。
默认从a开始dfs,依次顺延
输出格式
只有一行,包含n个字母,表示按照题目描述中的深度优先遍历算法遍历整个图的访问顶点顺序。
输入样例 复制
7
0 0 0 0 0 0 1
0 0 1 0 0 0 0
0 0 0 0 1 0 0
0 0 0 0 0 0 1
0 0 0 1 0 0 0
0 0 0 0 1 0 1
0 1 0 0 0 0 0
输出样例 复制
decbgaf
我当时有点不理解样例。。。
大家结合代码看一下下面的图吧
code:
#include<iostream>
#include<string>
#include<cstdio>
#include<stack>
#include<queue>
#include<bits/stdc++.h>
#define MAX_SIZE 100
using namespace std;
//邻接矩阵存储的图
struct Graph
{
int vexNumber;
string vexInfo[MAX_SIZE];
int adjMatrix[MAX_SIZE][MAX_SIZE];
};
//查找v0的未被访问的邻接点
int findAdjVex(const Graph& G,int v0,int visited[]){
for(int i=0;i<G.vexNumber;i++){
if(visited[i]==0&&G.adjMatrix[v0][i])return i;
}
return -1;
}
//以某个节点为起点求解DFS完成序(邻接矩阵)
string DFS_finished(const Graph& G,int v0,int visited[]){
string s="";
s.push_back(v0+'a');
visited[v0]=1;
for(int i=0;i<G.vexNumber;i++){
if(!visited[i]&&G.adjMatrix[v0][i])s+=DFS_finished(G,i,visited);
}
return s;
}
string DFS_finished(const Graph &G){
string s="";
int v[MAX_SIZE]={0};
// while(s.size()!=G.vexNumber)
for(int i=0;i<G.vexNumber;i++){
if(!v[i]){
string re=DFS_finished(G,i,v);
s.push_back(re[re.size()-1]);
memset(v,0,sizeof v);
for(int i=0;i<s.size();i++){
v[s[i]-'a']=1;
}
for(int i=re.size()-1;i>=0;i--){
if(!v[re[i]-'a']){
string ans=DFS_finished(G,re[i]-'a',v);
s.push_back(ans[ans.size()-1]);
memset(v,0,sizeof v);
for(int i=0;i<s.size();i++){
v[s[i]-'a']=1;
}
}
}
}
}
return s;
}
/*int main(){
Graph G;
cin>>G.vexNumber;
for(int i=0;i<G.vexNumber;i++){
G.vexInfo[i]=(char)(i+'a');
for(int j=0;j<G.vexNumber;j++){
cin>>G.adjMatrix[i][j];
}
}
string str=DFS_finished(G);
cout<<str<<endl;
return 0;
}*/
问题 H: 案例6-1.1:DFS应用-计算可达城市对
题目描述
共有N个城市编号1到N和M条路编号1到M。
第i条路可以从城市Ai通往Bi,但不能从Bi通往Ai。
你计划从某个城市出发经过X(X>=0)条路到某个城市,即计划从某个城市经过任意条路到另一个城市,终点可以是出发的城市。
请计算有多少对城市可以作为你的起点和终点。
输入格式
第一行为N和M。
接下来M行,每行有一对城市Ai和Bi
输出格式
请计算有多少对城市可以作为你的起点和终点。
输入样例 复制
3 3
1 2
2 3
3 2
输出样例 复制
7
数据范围与提示
2≤N≤2000
0≤M≤min(2000,N(N−1))
1≤Ai,Bi≤N
Ai≠Bi
样例解释:1城可选择去的城市(1,2,3),2城可选择(2,3),3城可选择(2,3),共七种选择
下面几题的思路都在代码框里
code:
/*计算可达城市对*/
/*题目大意就是A能到B,B能到C,那么A就能到C,很好理解对吧,没错,题意很简单
思路:这道题求可到达的城市对数,本来暴力,虽然感觉时间会超,但万一对了呢,
然后发现是我想多了。我最后的思路是将某一城市能到达的城市都放到一个容器中,
然后每个容器大小相加即可*/
#include <bits/stdc++.h>
using namespace std;
set<int>s[2005];//选择集合set存储能到达的城市,因为能去重
int main() {
int n, m;
cin >> n >> m;
int ans = 0;
for (int i = 1; i <= n; i++)
s[i].insert(i);//自己肯定能到自己
while (m--) {
int a, b;
cin >> a >> b;
s[a].insert(b);//有向图
//s[b].insert(a);
}
//更新每个容器,对于每个容器,其元素能到达的城市,它肯定也能到达,直接放进去就好
//注意要这样操作两次,原因可以自己想想(第一次提交就操作一次,再复制一遍就AC了hhh)
for (int i = 1; i <= n; i++) {
for (auto it = s[i].begin(); it != s[i].end(); it++) {
for (auto t2 = s[*it].begin(); t2 != s[*it].end(); t2++) {
//cout<<*t2<<endl;
s[i].insert(*t2);
}
}
}
for (int i = 1; i <= n; i++) {
for (auto it = s[i].begin(); it != s[i].end(); it++) {
for (auto t2 = s[*it].begin(); t2 != s[*it].end(); t2++) {
//cout<<*t2<<endl;
s[i].insert(*t2);
}
}
}
for (int i = 1; i <= n; i++) {
/*for(auto it=s[i].begin();it!=s[i].end();it++){
cout<<*it<<" ";
}
cout<<endl;*/
ans += s[i].size();
}
cout << ans << endl;
return 0;
}
问题 I: 案例6-1.3:哥尼斯堡的“七桥问题”
题目描述
哥尼斯堡是位于普累格河上的一座城市,它包含两个岛屿及连接它们的七座桥,如下图所示。
可否走过这样的七座桥,而且每桥只走过一次?瑞士数学家欧拉(Leonhard Euler,1707—1783)最终解决了这个问题,并由此创立了拓扑学。
这个问题如今可以描述为判断欧拉回路是否存在的问题。欧拉回路是指不令笔离开纸面,可画过图中每条边仅一次,且可以回到起点的一条回路。现给定一个无向图,问是否存在欧拉回路?
输入格式
输入第一行给出两个正整数,分别是节点数N (1≤N≤1000)和边数M;随后的M行对应M条边,每行给出一对正整数,分别是该条边直接连通的两个节点的编号(节点从1到N编号)。
输出格式
若欧拉回路存在则输出1,否则输出0。
输入样例 复制
6 10
1 2
2 3
3 1
4 5
5 6
6 4
1 4
1 6
3 4
3 6
输出样例 复制
1
听舍友说很简单就没仔细读题。。。
code:
/*题目没怎么看就写了,我猜大致意思就是问能否都走一遍再回来*/
/*这个问题我觉得对于每个点,出去的次数应该与回来的次数相同,不知道这样说能不能懂。
那么这个无向图的每个顶点,与其他节点有边的数目都是偶数才行*/
#include<bits/stdc++.h>
using namespace std;
set<int>v[2005];
int main(){
int n,m;
cin>>n>>m;
while(m--){
int a,b;
cin>>a>>b;
v[a].insert(b);
v[b].insert(a);
}
for(int i=1;i<=n;i++){
if((v[i].size()%2||v[i].empty())&&n!=1){//注意当存在节点与其他点都不连通并且节点个数
//不等于1时,也不能构成回路
cout<<0<<endl;
return 0;
}
}
cout<<1<<endl;
return 0;
}
问题 J: 案例6-1.4:地下迷宫探索
题目描述
地道战是在抗日战争时期,在华北平原上抗日军民利用地道打击日本侵略者的作战方式。地道网是房连房、街连街、村连村的地下工事,如下图所示。
我们在回顾前辈们艰苦卓绝的战争生活的同时,真心钦佩他们的聪明才智。在现在和平发展的年代,对多数人来说,探索地下通道或许只是一种娱乐或者益智的游戏。本实验案例以探索地下通道迷宫作为内容。
假设有一个地下通道迷宫,它的通道都是直的,而通道所有交叉点(包括通道的端点)上都有一盏灯和一个开关。请问你如何从某个起点开始在迷宫中点亮所有的灯并回到起点?
输入格式
输入第一行给出三个正整数,分别表示地下迷宫的节点数N(1<N≤1000,表示通道所有交叉点和端点)、边数M(≤3000,表示通道数)和探索起始节点编号S(节点从1到N编号)。随后的M行对应M条边(通道),每行给出一对正整数,分别是该条边直接连通的两个节点的编号。
输出格式
若可以点亮所有节点的灯,则输出从S开始并以S结束的包含所有节点的序列,序列中相邻的节点一定有边(通道);否则虽然不能点亮所有节点的灯,但还是输出点亮部分灯的节点序列,最后输出0,此时表示迷宫不是连通图。
由于深度优先遍历的节点序列是不唯一的,为了使得输出具有唯一的结果,我们约定以节点小编号优先的次序访问(点灯)。在点亮所有可以点亮的灯后,以原路返回的方式回到起点。
输入样例 复制
6 8 1
1 2
2 3
3 4
4 5
5 6
6 4
3 6
1 5
输出样例 复制
1 2 3 4 5 6 5 4 3 2 1
code:
/*地下迷宫探索*/
/*和无向图的遍历很像,不同的是,这个要回到起点,而且回退的过程也不同。
一次只能向前回退一个点*/
#include <bits/stdc++.h>
using namespace std;
int v[1005];//记录顶点的访问情况
int a[1005][1005];//邻接矩阵
int n;
int num;//记录访问的点的个数,判断是否完全遍历
int ans[2005];
void dfs(int x) {
//ans[num++]=x;
cout << x << " ";
v[x] = 1; //标记x被访问
num++;
for (int i = 1; i <= n; i++) {
if (!v[i] && a[x][i]) { //当i未被访问并且于当前访问顶点有边,可以向下深搜,但是完成之后是回退到x
dfs(i);
cout << x << " "; //只能回退一个
}
}
}
int main() {
int m, x;
cin >> n >> m >> x;
while (m--) {
int i, j;
cin >> i >> j;
a[i][j] = 1;
a[j][i] = 1;
}
dfs(x);
if (num != n)
cout << 0 << endl;
}
问题 K: 基础实验6-2.3:拯救007
题目描述
在老电影“007之生死关头”(Live and Let Die)中有一个情节,007被毒贩抓到一个鳄鱼池中心的小岛上,他用了一种极为大胆的方法逃脱 —— 直接踩着池子里一系列鳄鱼的大脑袋跳上岸去!(据说当年替身演员被最后一条鳄鱼咬住了脚,幸好穿的是特别加厚的靴子才逃过一劫。)
设鳄鱼池是长宽为100米的方形,中心坐标为 (0, 0),且东北角坐标为 (50, 50)。池心岛是以 (0, 0) 为圆心、直径15米的圆。给定池中分布的鳄鱼的坐标、以及007一次能跳跃的最大距离,你需要告诉他是否有可能逃出生天。
输入格式
首先第一行给出两个正整数:鳄鱼数量 N(≤100)和007一次能跳跃的最大距离 D 。随后 N 行,每行给出一条鳄鱼的 (x,y ) 坐标。
注意:不会有两条鳄鱼待在同一个点上。
输出格式
如果007有可能逃脱,就在一行中输出"Yes",否则输出"No"。
输入样例 复制
14 20
25 -15
-25 28
8 49
29 15
-35 -2
5 28
27 -29
-8 -28
-20 -35
-25 -20
-13 29
-30 15
-35 40
12 12
输出样例 复制
Yes
思路:还是dfs,每次踩着鳄鱼的头,下一步能跳出范围就可以结束,然后需要判断一下007跳跃范围内是否有跳板(鳄鱼),注意第一次跳跃的范围是半径为(7.5+d)的圆面。
这题要感谢xf同学()。
code:
#include<bits/stdc++.h>
using namespace std;
int vis[1005][1005];
int a[1005][1005];
int n,d;
int num;
int ans[2005];
int dfs(int x,int y){
if(x-d<=0||x+d>=100||y+d>=100||y-d<=0) return 1;
vis[x][y]=1;
for(int i=0;i<=100;i++){
for(int j=0;j<=100;j++){
if(!vis[i][j]&&a[i][j]&&(i-x)*(i-x)+(j-y)*(j-y)<=d*d){
return dfs(i,j);
// if(dfs(i,j))return 1;
//vis[i][j]=0;
}
}
}
return 0;
}
int main(){
cin>>n>>d;
while(n--){
int x,y;
cin>>x>>y;
a[x+50][y+50]=1;
//v[x].push_back(y);
}
for(int i=0;i<=100;i++){
for(int j=0;j<=100;j++){
if(!vis[i][j]&&a[i][j]&&(i-50)*(i-50)+(j-50)*(j-50)<=(double)(7.5+d)*(7.5+d)){
if(dfs(i,j)){
cout<<"Yes"<<endl;
return 0;
}
//vis[i][j]=0;
}
}
}
cout<<"No"<<endl;
}