/*8647 实现图的存储结构
Description
实现有向图的邻接矩阵存储结构。
输入格式
第一行:输入图的顶点个数n(各个顶点的默认编号为1~n), 边的条数m。
第二 ~ m+1行:每行输入两个顶点编号i、j,表示连接顶点i到顶点j的一条边。
输出格式
分n行输出n*n的邻接矩阵,表示所输入的图存储,顶点i和顶点j之间如果有边相连,则输出1,没边相连则输出0。
输入样例
4 4
1 2
1 3
3 4
4 1
输出样例
0 1 1 0
0 0 0 0
0 0 0 1
1 0 0 0*/
#include <iostream>
using namespace std;
const int N = 110;
int n, m;
int g[N][N];
int main()
{
// 输入n和m
cin >> n >> m;
// 读入m条边
while (m -- )
{
int a, b;
cin >> a >> b;
// 存储图中的边
g[a][b] = 1;
}
// 输出邻接矩阵
for (int i = 1; i <= n; i ++ )
{
for (int j = 1; j <= n; j ++ )
// 输出g[i][j]
cout << g[i][j] << ' ';
// 换行
cout << endl;
}
return 0;
}
/*8648 图的深度遍历
Description 实现图的邻接表存储结构及一些基本操作函数。在此基础上实现图的深度遍历算法并加以测试。本题只给出部分代码,请补全内容。
输入格式
第一行:输入0到3之间整数(有向图:0,有向网:1,无向图:2,无向网:3);
第二行:输入顶点数和边数;
第三行:输入各个顶点的值(字符型,长度〈3);(遍历从输入的第一个顶点开始)
第四行:输入每条弧(边)弧尾和弧头(以空格作为间隔),如果是网还要输入权值;
输出格式
输出对图深度遍历的结果。
输入样例
0
3 3
a b c
a b
b c
c b
输出样例
a b c
*/
#include"string.h"
#include"malloc.h" /* malloc()等 */
#include"stdio.h" /* EOF(=^Z或F6),NULL */
#include"stdlib.h" /* exit() */
typedef int InfoType; /* 顶点权值类型 */
#define MAX_NAME 3 /* 顶点字符串的最大长度+1 */
typedef char VertexType[MAX_NAME]; /* 字符串类型 */
/*图的邻接表存储表示 */
#define MAX_VERTEX_NUM 20
typedef enum{DG,DN,AG,AN}GraphKind; /* {有向图,有向网,无向图,无向网} */
typedef struct ArcNode
{
int adjvex; /* 该弧所指向的顶点的位置 */
struct ArcNode *nextarc; /* 指向下一条弧的指针 */
InfoType *info; /* 网的权值指针) */
}ArcNode; /* 表结点 */
typedef struct
{
VertexType data; /* 顶点信息 */
ArcNode *firstarc; /* 第一个表结点的地址,指向第一条依附该顶点的弧的指针 */
}VNode,AdjList[MAX_VERTEX_NUM]; /* 头结点 */
typedef struct
{
AdjList vertices;
int vexnum,arcnum; /* 图的当前顶点数和弧数 */
int kind; /* 图的种类标志 */
}ALGraph;
int LocateVex(ALGraph G,VertexType u)
{ /* 初始条件: 图G存在,u和G中顶点有相同特征 */
/* 操作结果: 若G中存在顶点u,则返回该顶点在图中位置;否则返回-1 */
int i;
for(i=0;i<G.vexnum;++i)
if(strcmp(u,G.vertices[i].data)==0)
return i;
return -1;
}
void CreateGraph(ALGraph *G)
{ /* 采用邻接表存储结构,构造没有相关信息的图G(用一个函数构造4种图) */
int i,j,k;
int w; /* 权值 */
VertexType va,vb;
ArcNode *p;
//printf("Enter the type of map:(0~3): ");
scanf("%d",&(*G).kind);
//printf("Enter Vertex number,Arc number: ");
scanf("%d%d",&(*G).vexnum,&(*G).arcnum);
//printf("Enter %d Vertex :\n",(*G).vexnum);
for(i=0;i<(*G).vexnum;++i) /* 构造顶点向量 */
{
scanf("%s",(*G).vertices[i].data);
(*G).vertices[i].firstarc=NULL;
}
//if((*G).kind==1||(*G).kind==3) /* 网 */
// printf("Enter order every arc weight,head and tail (Takes the gap by the blank space ):\n");
//else /* 图 */
// printf("Enter order every arc head and tail (Takes the gap by the blank space ):\n");
for(k=0;k<(*G).arcnum;++k) /* 构造表结点链表 */
{
scanf("%s %s", va, vb);
i = LocateVex(*G, va);
j = LocateVex(*G, vb);
p = (ArcNode*)malloc(sizeof(ArcNode));
p->adjvex = j;
p->nextarc = (*G).vertices[i].firstarc;
(*G).vertices[i].firstarc = p;
if ((*G).kind == 1 || (*G).kind == 3) {
p->info = (int*)malloc(sizeof(int));
scanf("%d", p->info);
}
}
}
void DFS(ALGraph G, int v, int visited[]) {
visited[v] = 1;
printf("%s ", G.vertices[v].data);
ArcNode* p = G.vertices[v].firstarc;
while (p) {
if (!visited[p->adjvex]) {
DFS(G, p->adjvex, visited);
}
p = p->nextarc;
}
}
void DFSTraverse(ALGraph G) {
int visited[MAX_VERTEX_NUM];
for (int i = 0; i < G.vexnum; i++) {
visited[i] = 0;
}
for (int i = 0; i < G.vexnum; i++) {
if (!visited[i]) {
DFS(G, i, visited);
}
}
}
int main() {
ALGraph G;
CreateGraph(&G);
DFSTraverse(G);
return 0;
}
/*18448 最小生成树
Description
给定结点数为n,边数为m的带权无向连通图G,所有结点编号为1,2,3....n。
求图G的最小生成树的边权和。
输入格式
第一行两个正整数n和m。n,m<=2000
之后的m行,每行三个正整数a,b,w,描述一条连接结点a和b,边权为w的边。1=<a,b<=n,w<=10^18。
注意可能存在重边和自环。
输出格式
一个整数表示图G的最小生成树的边权和(注意用长整型)。
输入样例
7 12
1 2 9
1 5 2
1 6 3
2 3 5
2 6 7
3 4 6
3 7 3
4 5 6
4 7 2
5 6 3
5 7 6
6 7 1
输出样例
16*/
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 2010, M = 4000010;
int n, m;
int h[N], e[M], ne[M], w[M], idx; // h[i]表示第i个点的所有边的编号,e[i]表示第i条边的另一个端点,ne[i]表示第i条边的下一条边的编号,w[i]表示第i条边的权重,idx表示当前边的编号
int dist[N]; // dist[i]表示第i个点到最小生成树的距离
bool st[N]; // st[i]表示第i个点是否已经在最小生成树中
void add(int a, int b, int c) // 添加一条边a->b,边权为c
{
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ; // 将边的信息存储在数组中,同时更新h数组和idx
}
long long prim() // 返回最小生成树的边权和,如果图不连通则返回0x3f3f3f3f3f3f3f3f
{
memset(dist, 0x3f, sizeof dist); // 初始化dist数组为无穷大
long long res = 0; // 存储最小生成树的边权和
for (int i = 0; i < n; i ++ ) // 执行n次
{
int t = -1; // t表示距离最小生成树最近的点
for (int j = 1; j <= n; j ++ ) // 找到距离最小生成树最近的点
if (!st[j] && (t == -1 || dist[t] > dist[j]))
t = j;
if (i && dist[t] == 0x3f3f3f3f) return 0x3f3f3f3f3f3f3f3f; // 如果t没有被更新过,说明图不连通,返回无穷大
if (i) res += dist[t]; // 将t到最小生成树的距离加入res
st[t] = true; // 将t加入最小生成树
for (int j = h[t]; ~j; j = ne[j]) // 更新t的所有出边
{
int k = e[j]; // k表示t的一个出边的另一个端点
if (!st[k]) dist[k] = min(dist[k], w[j]); // 如果k不在最小生成树中,更新k到最小生成树的距离
}
}
return res; // 返回最小生成树的边权和
}
int main()
{
memset(h, -1, sizeof h); // 初始化h数组为-1
cin >> n >> m; // 输入点数和边数
while (m -- ) // 输入每一条边
{
int a, b, c;
cin >> a >> b >> c;
add(a, b, c), add(b, a, c); // 添加双向边
}
cout << prim() << endl; // 输出最小生成树的边权和
return 0;
}
/*18732 最短路问题
Description
现在有n个车站和m条直达公交线路,每条线路都有一个固定票价。
作为一个窮人,你打算从车站1坐车到车站n,请计算下车站1到车站n的最少花费。
如果车站1无法到达车站n,请输出-1。
注意,在车站x和y之间可能存在不止一条线路。
输入格式
第一行两个整数n和m,表示车站数量和线路数量。(1<=n<=100),(1<=m<=1000)
第二行至第m+1行,每行3个整数a,b,x,代表车站a和车站b之间有一条票价为x的公交线路,公交线路是双向的。
输出格式
输出车站1到n的最小花费。
输入样例
4 4
1 2 4
2 3 7
2 4 1
3 4 6
输出样例
5*/
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 110, M = 2010;
int n, m;
int h[N], e[M], w[M], ne[M], idx;
int dist[N]; // dist[i]表示1号点到i号点的最短距离
bool st[N]; // st[i]表示i号点是否已经确定最短路
void add(int a, int b, int c)
{
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ; // 添加一条a到b的边,边权为c
}
int dijkstra()
{
memset(dist, 0x3f, sizeof dist); // 初始化,1号点到其它各个点的距离均为正无穷
dist[1] = 0; // 1号点到1号点的距离为0
for (int i = 0; i < n; i ++ ) // 进行n次操作
{
int t = -1; // 在还未确定最短路的点中,寻找距离最小的点
for (int j = 1; j <= n; j ++ )
if (!st[j] && (t == -1 || dist[t] > dist[j]))
t = j;
if (t == -1) break; // 如果没有找到,说明剩下的点和起点不连通
st[t] = true; // 确定一个点的最短路
for (int j = h[t]; ~j; j = ne[j]) // 用t更新其他点的距离
{
int ver = e[j], distance = w[j];
if (dist[ver] > dist[t] + distance)
dist[ver] = dist[t] + distance; // 通过t进行松弛操作
}
}
if (dist[n] == 0x3f3f3f3f) return -1; // 无法从1号点走到n号点
return dist[n]; // 返回1号点到n号点的最短距离
}
int main()
{
memset(h, -1, sizeof h); // 初始化邻接表
cin >> n >> m; // n表示点数,m表示边数
while (m -- )
{
int a, b, c;
cin >> a >> b >> c; // 读入一条边
add(a, b, c), add(b, a, c); // 存储无向图
}
cout << dijkstra() << endl; // 输出1号点到n号点的最短距离
return 0;
}
/*
标程
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
int n,m,v[105],d[105];
struct node
{
int adj,val;
};
vector<node>e[105];
int getmin()
{
int i,minv=999999999,mini=0;
for(i=1; i<=n; i++)
{
if(v[i]==0&&minv>d[i])
{
mini=i;
minv=d[i];
}
}
return mini;
}
void djstra(int s)
{
int i,j;
memset(d,127/3,sizeof(d));
d[s]=0;
for(i=1; i<=n; i++)
{
int temp=getmin();
v[temp]=1;
for(j=0; j<e[temp].size(); j++) /**< e[1] 存的是2 4 5,e[1][j]
{
int x=e[temp][j].adj,z=e[temp][j].val;
if(v[x]==0&&d[x]>d[temp]+z)
{
d[x]=d[temp]+z;
}
}
}
}
int main()
{
ios::sync_with_stdio(0),cin.tie(0);
int i,j,x,y,z,ans=0;
cin>>n>>m;
for(i=1; i<=m; i++)
{
cin>>x>>y>>z;
e[x].push_back({y,z});
e[y].push_back({x,z});
}
djstra(1);
if(d[n]<1e8)
cout<<d[n];
else
cout<<-1;
return 0;
}
*/
/*8649 图的广度遍历
Description
使用图的深度遍历实现的邻接表存储结构和基本操作函数,在此基础上实现图的广度遍历算法并加以测试。注意正确使用队列存储结构。
输入格式
第一行:输入0到3之间整数(有向图:0,有向网:1,无向图:2,无向网:3);
第二行:输入顶点数和边数;
第三行:输入各个顶点的值(字符型,长度〈3);(遍历从输入的第一个顶点开始)
第四行:输入每条弧(边)弧尾和弧头(以空格作为间隔),如果是网还要输入权值;
输出格式
输出对图广度遍历的结果
输入样例
0
3 3
a b c
a b
b c
c b
输出样例
a b c
提示
注意题目的邻接表采用头插法,也就是后出现的边节点插入到邻接表的表头。
*/
#include"string.h"
#include"malloc.h" /* malloc()等 */
#include"stdio.h" /* EOF(=^Z或F6),NULL */
#include"stdlib.h" /* exit() */
#include <queue>
using namespace std;
typedef int InfoType; /* 顶点权值类型 */
#define MAX_NAME 3 /* 顶点字符串的最大长度+1 */
typedef char VertexType[MAX_NAME]; /* 字符串类型 */
/*图的邻接表存储表示 */
#define MAX_VERTEX_NUM 20
typedef enum{DG,DN,AG,AN}GraphKind; /* {有向图,有向网,无向图,无向网} */
typedef struct ArcNode
{
int adjvex; /* 该弧所指向的顶点的位置 */
struct ArcNode *nextarc; /* 指向下一条弧的指针 */
InfoType *info; /* 网的权值指针) */
}ArcNode; /* 表结点 */
typedef struct
{
VertexType data; /* 顶点信息 */
ArcNode *firstarc; /* 第一个表结点的地址,指向第一条依附该顶点的弧的指针 */
}VNode,AdjList[MAX_VERTEX_NUM]; /* 头结点 */
typedef struct
{
AdjList vertices;
int vexnum,arcnum; /* 图的当前顶点数和弧数 */
int kind; /* 图的种类标志 */
}ALGraph;
int LocateVex(ALGraph G,VertexType u)
{ /* 初始条件: 图G存在,u和G中顶点有相同特征 */
/* 操作结果: 若G中存在顶点u,则返回该顶点在图中位置;否则返回-1 */
int i;
for(i=0;i<G.vexnum;++i)
if(strcmp(u,G.vertices[i].data)==0)
return i;
return -1;
}
void CreateGraph(ALGraph *G)
{ /* 采用邻接表存储结构,构造没有相关信息的图G(用一个函数构造4种图) */
int i,j,k;
int w; /* 权值 */
VertexType va,vb;
ArcNode *p;
//printf("Enter the type of map:(0~3): ");
scanf("%d",&(*G).kind);
//printf("Enter Vertex number,Arc number: ");
scanf("%d%d",&(*G).vexnum,&(*G).arcnum);
//printf("Enter %d Vertex :\n",(*G).vexnum);
for(i=0;i<(*G).vexnum;++i) /* 构造顶点向量 */
{
scanf("%s",(*G).vertices[i].data);
(*G).vertices[i].firstarc=NULL;
}
//if((*G).kind==1||(*G).kind==3) /* 网 */
// printf("Enter order every arc weight,head and tail (Takes the gap by the blank space ):\n");
//else /* 图 */
// printf("Enter order every arc head and tail (Takes the gap by the blank space ):\n");
for(k=0;k<(*G).arcnum;++k) /* 构造表结点链表 */
{
if((*G).kind==1||(*G).kind==3) /* 网 */
scanf("%d%s%s",&w,va,vb);
else /* 图 */
scanf("%s%s",va,vb);
i=LocateVex(*G,va); /* 弧尾 */
j=LocateVex(*G,vb); /* 弧头 */
p=(ArcNode*)malloc(sizeof(ArcNode));
p->adjvex=j;
if((*G).kind==1||(*G).kind==3) /* 网 */
{
p->info=(int *)malloc(sizeof(int));
*(p->info)=w;
}
else
p->info=NULL; /* 图 */
p->nextarc=(*G).vertices[i].firstarc; /* 插在表头 */
(*G).vertices[i].firstarc=p;
if((*G).kind>=2) /* 无向图或网,产生第二个表结点 */
{
p=(ArcNode*)malloc(sizeof(ArcNode));
p->adjvex=i;
if((*G).kind==3) /* 无向网 */
{
p->info=(int*)malloc(sizeof(int));
*(p->info)=w;
}
else
p->info=NULL; /* 无向图 */
p->nextarc=(*G).vertices[j].firstarc; /* 插在表头 */
(*G).vertices[j].firstarc=p;
}
}
}
VertexType* GetVex(ALGraph G,int v)
{ /* 初始条件: 图G存在,v是G中某个顶点的序号。操作结果: 返回v的值 */
if(v>=G.vexnum||v<0)
exit(0);
return &G.vertices[v].data;
}
int FirstAdjVex(ALGraph G,VertexType v)
{ /* 初始条件: 图G存在,v是G中某个顶点 */
/* 操作结果: 返回v的第一个邻接顶点的序号。若顶点在G中没有邻接顶点,则返回-1 */
ArcNode *p;
int v1;
v1=LocateVex(G,v); /* v1为顶点v在图G中的序号 */
p=G.vertices[v1].firstarc;
if(p)
return p->adjvex;
else
return -1;
}
int NextAdjVex(ALGraph G,VertexType v,VertexType w)
{ /* 初始条件: 图G存在,v是G中某个顶点,w是v的邻接顶点 */
/* 操作结果: 返回v的(相对于w的)下一个邻接顶点的序号。 */
/* 若w是v的最后一个邻接点,则返回-1 */
ArcNode *p;
int v1,w1;
v1=LocateVex(G,v); /* v1为顶点v在图G中的序号 */
w1=LocateVex(G,w); /* w1为顶点w在图G中的序号 */
p=G.vertices[v1].firstarc;
while(p&&p->adjvex!=w1) /* 指针p不空且所指表结点不是w */
p=p->nextarc;
if(!p||!p->nextarc) /* 没找到w或w是最后一个邻接点 */
return -1;
else /* p->adjvex==w */
return p->nextarc->adjvex; /* 返回v的(相对于w的)下一个邻接顶点的序号 */
}
int visited[MAX_VERTEX_NUM]; /* 访问标志数组(全局量),未访问标记0,访问标记1 */
void(*VisitFunc)(char* v); /* 函数变量(全局量) */
void BFSTraverse(ALGraph G,void(*Visit)(char*)) //广搜
{
int v;
VisitFunc=Visit;
queue<int> Q;
memset(visited,0,sizeof(visited));
for(v=0;v<G.vexnum;++v)
{
if(!visited[v])
{
visited[v]=1;
Q.push(v);
VisitFunc(G.vertices[v].data);
while(!Q.empty())
{
int t,w;
t=Q.front();
Q.pop();
for(w=FirstAdjVex(G,G.vertices[t].data);w>=0;w=NextAdjVex(G,G.vertices[t].data,G.vertices[w].data))
{
if(!visited[w])
{
visited[w]=1;
VisitFunc(G.vertices[w].data);
Q.push(w);
}
}
}
}
}
printf("\n");
}
void print(char *i)
{
printf("%s ",i);
}
int main()
{
ALGraph g;
CreateGraph(&g);
BFSTraverse(g,print);
return 1;
}
/*18734 拓扑排序
在经历.....之后,你打算好好学习下计算机专业的课程,避免面试过程中的各种尴尬场面。
计算机的专业课程间既有循序渐进的特点,相互间也存在着依赖关系(似乎其他专业也是这样......)。
现在给你n门课程和m个课程间关系,请给出一个有效的学习次序。
注意可能存在多门课程不依赖任何其他课程
输入格式
第一行有2个数,分别为课程数n和关系数m。 (1=<n<=20) (1=<m<=30)
接下来有m行,每一行有2个整数a和b,表示课程b依赖于课程a。(1=<a,b<=n)
输出格式
仅一行,一个整数序列,代表课程学习次序。
为确保输出唯一性,同等条件下,编号小的在排在前面。
输入样例
6 8
1 2
1 3
1 4
3 2
3 5
4 5
6 4
6 5
输出样例
1 3 2 6 4 5*/
#include<iostream>
#include<queue>
#include<algorithm>
#include<vector>
using namespace std;
vector<int> v[30];
int a[30],c[30];
int main()
{
int n,m,cnt=0;
cin>>n>>m;
for(int i=1;i<=m;i++)
{
int x,y;
cin>>x>>y;
v[x].push_back(y);
a[y]++;
}
priority_queue<int,vector<int>,greater<int> >q;
for(int i=1;i<=n;i++)
{
if(!a[i])
q.push(i);
}
while(!q.empty())
{
int x=q.top();
q.pop();
for(int i=0;i<v[x].size();i++)
{
a[v[x][i]]--;
if(!a[v[x][i]])
q.push(v[x][i]);
}
c[++cnt]=x;
}
for(int i=1;i<=cnt;i++)cout<<c[i]<<" ";
return 0;
}
/*#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
int main()
{
ios::sync_with_stdio(0),cin.tie(0);
int i,j,n,m,a[25][25]= {0},d[105]= {0},x,y;
cin>>n>>m;
for(i=1; i<=m; i++)
{
cin>>x>>y;
a[x][y]=1;
d[y]++;
}
set<int>st;
for(i=1; i<=n; i++)
if(d[i]==0)
st.insert(i);
while(!st.empty())
{
int t=*st.begin();
cout<<t<<' ';
st.erase(st.begin());
for(i=1; i<=n; i++)
{
if(a[t][i])
{
d[i]--;
if(d[i]==0)
st.insert(i);
}
}
}
return 0;
}
*/
/*18747 关键路径
Description
在一个工程项目里,多项工作可以同时进行。
我们可以用有向无环图表述项目流程,把项目中的事件表述为结点,把活动表述成有权值的边。
现在我们已知项目共有n个事件,起点为1,终点为n,m个活动。
请你计算出这个项目的最早完成时间,也就是起点到收点的最长路径,即关键路径。
输入格式
第一行两个整数n和m,代表结点数量和边数量。(1<=n,m<=100)
下面m行,每行3个整数a,b,x,表示点a到点b之间有一条长度为x的有向边。
输出格式
一个整数,起点到终点的最长路径.
输入样例
4 6
1 2 3
1 3 2
1 4 3
2 3 3
2 4 5
3 4 3
输出样例
9*/
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <queue>
using namespace std;
int n,m,d[1005]= {0},e[1005][1005],dp[1005]; //dp动态规划
int main()
{
cin >> n >> m;
int i,x,y,z;
for(i=1; i<=m; i++)
{
cin >> x >> y >> z;
e[x][y]=z;//构建邻接矩阵,赋权值
d[y]++;//入度数+1
}
queue<int>q;
for(i=1; i<=n; i++)
{
if(d[i]==0)//如果入度数为0
q.push(i);//入队
}
while(q.size())
{
int t=q.front();//取队头元素
//cout << t << ' ';
q.pop();//弹出
for(i=1; i<=n; i++) //n个顶点
{
if(e[t][i])
{
dp[i]=max(dp[i],dp[t]+e[t][i]);//求从点1到点i的最长路径
d[i]--;//入度数-1
if(d[i]==0)//如果入度数为0
q.push(i);//入队
}
}
}
cout << dp[n];//输出从起点1到终点n的最长路径
return 0;
}
/*
标程
#include <iostream>
#include <queue>
using namespace std;
int n,m,a[105][105],v[1005],d[1005],dis[1005];
int main()
{
ios::sync_with_stdio(0),cin.tie(0);
int i,j,x,y,z;
cin>>n>>m;
for(i=1; i<=m; i++)
{
cin>>x>>y>>z;
a[x][y]=z;
d[y]++;
}
queue<int>q;
q.push(1);
while(!q.empty())
{
int t=q.front();
q.pop();
for(i=1; i<=n; i++)
{
if(a[t][i])
{
d[i]--,dis[i]=max(dis[i],dis[t]+a[t][i]);
if(d[i]==0)
q.push(i);
}
}
}
cout<<dis[n];
return 0;
}
*/