7-1 列出连通集 (25 分)
给定一个有N个顶点和E条边的无向图,请用DFS和BFS分别列出其所有的连通集。假设顶点从0到N−1编号。进行搜索时,假设我们总是从编号最小的顶点出发,按编号递增的顺序访问邻接点。
输入格式:
输入第1行给出2个整数N(0<N≤10)和E,分别是图的顶点数和边数。随后E行,每行给出一条边的两个端点。每行中的数字之间用1空格分隔。
输出格式:
按照"{ v1 v2 … vk }"的格式,每行输出一个连通集。先输出DFS的结果,再输出BFS的结果。
输入样例:
8 6
0 7
0 1
2 0
4 1
2 4
3 5
输出样例:
{ 0 1 4 2 7 }
{ 3 5 }
{ 6 }
{ 0 1 2 7 4 }
{ 3 5 }
{ 6 }
Code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int a[15][15], vis[15];
int n, e;
void DFS(int x)
{
vis[x] = 1;
for (int i = 0; i < n; i++)
{
if (a[x][i] == 1 && vis[i] == 0)
{
printf(" %d", i);
DFS(i);
}
}
}
void BFS(int x)
{
int q[15];
int front = -1, rear = -1;
vis[x] = 1;
q[++rear] = x;
while (rear > front)
{
int t = q[++front];
for (int i = 0; i < n; i++)
{
if (a[t][i] == 1 && vis[i] == 0)
{
printf(" %d", i);
vis[i] = 1;
q[++rear] = i;
}
}
}
}
int main()
{
scanf("%d %d", &n, &e);
while (e--)
{
int u, v;
scanf("%d %d", &u, &v);
a[u][v] = 1;
a[v][u] = 1;
}
memset(vis, 0, sizeof(vis));
for (int i = 0; i < n; i++)
{
if (vis[i] == 0)
{
printf("{ %d", i);
DFS(i);
printf(" }\n");
}
}
memset(vis, 0, sizeof(vis));
for (int i = 0; i < n; i++)
{
if (vis[i] == 0)
{
printf("{ %d", i);
BFS(i);
printf(" }\n");
}
}
return 0;
}
7-2 哈利·波特的考试 (25 分)
哈利·波特要考试了,他需要你的帮助。这门课学的是用魔咒将一种动物变成另一种动物的本事。例如将猫变成老鼠的魔咒是haha,将老鼠变成鱼的魔咒是hehe等等。反方向变化的魔咒就是简单地将原来的魔咒倒过来念,例如ahah可以将老鼠变成猫。另外,如果想把猫变成鱼,可以通过念一个直接魔咒lalala,也可以将猫变老鼠、老鼠变鱼的魔咒连起来念:hahahehe。
现在哈利·波特的手里有一本教材,里面列出了所有的变形魔咒和能变的动物。老师允许他自己带一只动物去考场,要考察他把这只动物变成任意一只指定动物的本事。于是他来问你:带什么动物去可以让最难变的那种动物(即该动物变为哈利·波特自己带去的动物所需要的魔咒最长)需要的魔咒最短?例如:如果只有猫、鼠、鱼,则显然哈利·波特应该带鼠去,因为鼠变成另外两种动物都只需要念4个字符;而如果带猫去,则至少需要念6个字符才能把猫变成鱼;同理,带鱼去也不是最好的选择。
输入格式:
输入说明:输入第1行给出两个正整数N (≤100)和M,其中N是考试涉及的动物总数,M是用于直接变形的魔咒条数。为简单起见,我们将动物按1~N编号。随后M行,每行给出了3个正整数,分别是两种动物的编号、以及它们之间变形需要的魔咒的长度(≤100),数字之间用空格分隔。
输出格式:
输出哈利·波特应该带去考场的动物的编号、以及最长的变形魔咒的长度,中间以空格分隔。如果只带1只动物是不可能完成所有变形要求的,则输出0。如果有若干只动物都可以备选,则输出编号最小的那只。
输入样例:
6 11
3 4 70
1 2 1
5 4 50
2 6 50
5 6 60
1 3 70
4 6 60
3 6 80
5 1 100
2 4 60
5 2 80
输出样例:
4 70
Code:
#include <stdio.h>
#include <stdlib.h>
#define INF 0x3f3f3f3f
int a[105][105], n, m;
void floyd()
{
for (int k = 1; k <= n; k++)
{
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
if (a[i][j] > a[i][k] + a[k][j])
a[i][j] = a[i][k] + a[k][j];
}
}
}
int main()
{
scanf("%d %d", &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;
}
}
while (m--)
{
int u, v, w;
scanf("%d %d %d", &u, &v, &w);
a[u][v] = a[v][u] = w;
}
floyd();
int idx, ans = INF;
for (int i = 1; i <= n; i++)
{
int t = 0;
for (int j = 1; j <= n; j++)
{
if (a[i][j] > t)
t = a[i][j];
}
if (t < ans)
{
ans = t;
idx = i;
}
}
if (ans == INF)
printf("0\n");
else
printf("%d %d\n", idx, ans);
return 0;
}
7-3 旅游规划 (25 分)
有了一张自驾旅游路线图,你会知道城市间的高速公路长度、以及该公路要收取的过路费。现在需要你写一个程序,帮助前来咨询的游客找一条出发地和目的地之间的最短路径。如果有若干条路径都是最短的,那么需要输出最便宜的一条路径。
输入格式:
输入说明:输入数据的第1行给出4个正整数N、M、S、D,其中N(2≤N≤500)是城市的个数,顺便假设城市的编号为0~(N−1);M是高速公路的条数;S是出发地的城市编号;D是目的地的城市编号。随后的M行中,每行给出一条高速公路的信息,分别是:城市1、城市2、高速公路长度、收费额,中间用空格分开,数字均为整数且不超过500。输入保证解的存在。
输出格式:
在一行里输出路径的长度和收费总额,数字间以空格分隔,输出结尾不能有多余空格。
输入样例:
4 5 0 3
0 1 1 20
1 3 2 30
0 3 4 10
0 2 2 20
2 3 1 20
输出样例:
3 40
Code:
#include <stdio.h>
#include <stdlib.h>
#define INF 0x3f3f3f3f
struct
{
int length;
int price;
} a[505][505];
void floyd(int n)
{
for (int k = 0; k < n; k++)
{
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
if (a[i][j].length > a[i][k].length + a[k][j].length)
{
a[i][j].length = a[i][k].length + a[k][j].length;
a[i][j].price = a[i][k].price + a[k][j].price;
}
else if (a[i][j].length == a[i][k].length + a[k][j].length)
{
if (a[i][j].price > a[i][k].price + a[k][j].price)
a[i][j].price = a[i][k].price + a[k][j].price;
}
}
}
}
}
int main()
{
int n, m, s, d;
scanf("%d %d %d %d", &n, &m, &s, &d);
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
if (i == j)
a[i][j].length = a[i][j].price = 0;
else
a[i][j].length = a[i][j].price = INF;
}
}
while (m--)
{
int u, v, l, p;
scanf("%d %d %d %d", &u, &v, &l, &p);
a[u][v].length = a[v][u].length = l;
a[u][v].price = a[v][u].price = p;
}
floyd(n);
printf("%d %d\n", a[s][d].length, a[s][d].price);
return 0;
}
7-4 公路村村通 (30 分)
现有村落间道路的统计数据表中,列出了有可能建设成标准公路的若干条道路的成本,求使每个村落都有公路连通所需要的最低成本。
输入格式:
输入数据包括城镇数目正整数N(≤1000)和候选道路数目M(≤3N);随后的M行对应M条道路,每行给出3个正整数,分别是该条道路直接连通的两个城镇的编号以及该道路改建的预算成本。为简单起见,城镇从1到N编号。
输出格式:
输出村村通需要的最低成本。如果输入数据不足以保证畅通,则输出−1,表示需要建设更多公路。
输入样例:
6 15
1 2 5
1 3 3
1 4 7
1 5 4
1 6 2
2 3 4
2 4 6
2 5 2
2 6 6
3 4 6
3 5 1
3 6 1
4 5 10
4 6 8
5 6 3
输出样例:
12
Code:
#include <stdio.h>
#include <stdlib.h>
int fa[1005];
typedef struct
{
int l;
int r;
int weight;
} path;
path p[3005];
int n, m, sum, cnt;
int cmp(const void *a, const void *b)
{
path *p1 = (path *)a;
path *p2 = (path *)b;
return p1->weight - p2->weight;
}
int Find(int x)
{
return (x == fa[x]) ? (x) : (fa[x] = Find(fa[x]));
}
void Union(int x, int y)
{
fa[Find(x)] = Find(y);
}
int main()
{
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; i++)
fa[i] = i;
for (int i = 0; i < m; i++)
scanf("%d %d %d", &p[i].l, &p[i].r, &p[i].weight);
qsort(p, m, sizeof(path), cmp);
for (int i = 0; i < m; i++)
{
if (Find(p[i].l) != Find(p[i].r))
{
sum += p[i].weight;
Union(p[i].l, p[i].r);
cnt++;
}
if (cnt == n - 1)
break;
}
if (cnt == n - 1)
printf("%d\n", sum);
else
printf("-1\n");
return 0;
}
7-5 任务调度的合理性 (25 分)
假定一个工程项目由一组子任务构成,子任务之间有的可以并行执行,有的必须在完成了其它一些子任务后才能执行。“任务调度”包括一组子任务、以及每个子任务可以执行所依赖的子任务集。
比如完成一个专业的所有课程学习和毕业设计可以看成一个本科生要完成的一项工程,各门课程可以看成是子任务。有些课程可以同时开设,比如英语和C程序设计,它们没有必须先修哪门的约束;有些课程则不可以同时开设,因为它们有先后的依赖关系,比如C程序设计和数据结构两门课,必须先学习前者。
但是需要注意的是,对一组子任务,并不是任意的任务调度都是一个可行的方案。比如方案中存在“子任务A依赖于子任务B,子任务B依赖于子任务C,子任务C又依赖于子任务A”,那么这三个任务哪个都不能先执行,这就是一个不可行的方案。你现在的工作是写程序判定任何一个给定的任务调度是否可行。
输入格式:
输入说明:输入第一行给出子任务数N(≤100),子任务按1~N编号。随后N行,每行给出一个子任务的依赖集合:首先给出依赖集合中的子任务数K,随后给出K个子任务编号,整数之间都用空格分隔。
输出格式:
如果方案可行,则输出1,否则输出0。
输入样例1:
12
0
0
2 1 2
0
1 4
1 5
2 3 6
1 3
2 7 8
1 7
1 10
1 7
输出样例1:
1
输入样例2:
5
1 4
2 1 4
2 2 5
1 3
0
输出样例2:
0
Code:
#include <stdio.h>
#include <stdlib.h>
int n, cnt;
int ru[105], a[105][105];
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i++)
{
int m;
scanf("%d", &m);
while (m--)
{
int u;
scanf("%d", &u);
a[u][i] = 1;
ru[i]++;
}
}
int q[105];
int front = -1, rear = -1;
for (int i = 1; i <= n; i++)
{
if (ru[i] == 0)
q[++rear] = i;
}
while (rear > front)
{
int u = q[++front];
cnt++;
for (int i = 1; i <= n; i++)
{
if (a[u][i] == 1)
{
if (--ru[i] == 0)
q[++rear] = i;
}
}
}
if (cnt == n)
printf("1\n");
else
printf("0\n");
return 0;
}
7-6 家庭房产 (25 分)
给定每个人的家庭成员和其自己名下的房产,请你统计出每个家庭的人口数、人均房产面积及房产套数。
输入格式:
输入第一行给出一个正整数N(≤1000),随后N行,每行按下列格式给出一个人的房产:
编号 父 母 k 孩子1 … 孩子k 房产套数 总面积
其中编号是每个人独有的一个4位数的编号;父和母分别是该编号对应的这个人的父母的编号(如果已经过世,则显示-1);k(0≤k≤5)是该人的子女的个数;孩子i是其子女的编号。
输出格式:
首先在第一行输出家庭个数(所有有亲属关系的人都属于同一个家庭)。随后按下列格式输出每个家庭的信息:
家庭成员的最小编号 家庭人口数 人均房产套数 人均房产面积
其中人均值要求保留小数点后3位。家庭信息首先按人均面积降序输出,若有并列,则按成员编号的升序输出。
输入样例:
10
6666 5551 5552 1 7777 1 100
1234 5678 9012 1 0002 2 300
8888 -1 -1 0 1 1000
2468 0001 0004 1 2222 1 500
7777 6666 -1 0 2 300
3721 -1 -1 1 2333 2 150
9012 -1 -1 3 1236 1235 1234 1 100
1235 5678 9012 0 1 50
2222 1236 2468 2 6661 6662 1 300
2333 -1 3721 3 6661 6662 6663 1 100
输出样例:
3
8888 1 1.000 1000.000
0001 15 0.600 100.000
5551 4 0.750 100.000
Code:
#include <stdio.h>
#include <stdlib.h>
#define N 10005
typedef struct
{
int id, num, cnt, area;
} home;
home a[N];
int fa[N], num[N], cnt[N], area[N], vis[N], n;
int Find(int x)
{
return (x == fa[x]) ? (x) : (fa[x] = Find(fa[x]));
}
void Union(int x, int y)
{
vis[x] = 1;
vis[y] = 1;
int a = Find(x);
int b = Find(y);
if (a < b)
{
fa[b] = a;
num[a] += num[b];
cnt[a] += cnt[b];
area[a] += area[b];
}
else if (a > b)
{
fa[a] = b;
num[b] += num[a];
cnt[b] += cnt[a];
area[b] += area[a];
}
}
int cmp(const void *a, const void *b)
{
home *h1 = (home *)a;
home *h2 = (home *)b;
if (h1->num * h2->area == h2->num * h1->area)
return h1->id - h2->id;
return h1->num * h2->area - h2->num * h1->area;
}
int main()
{
for (int i = 0; i < N; i++)
{
fa[i] = i;
num[i] = 1;
}
scanf("%d", &n);
for (int i = 0; i < n; i++)
{
int id, fa, ma;
scanf("%d %d %d", &id, &fa, &ma);
vis[id] = 1;
if (fa != -1)
Union(id, fa);
if (ma != -1)
Union(id, ma);
int k;
scanf("%d", &k);
while (k--)
{
int child;
scanf("%d", &child);
Union(id, child);
}
int x, y;
scanf("%d %d", &x, &y);
cnt[Find(id)] += x;
area[Find(id)] += y;
}
int k = 0;
for (int i = 0; i < N; i++)
{
if (Find(i) == i && vis[i])
{
a[k].id = i;
a[k].num = num[i];
a[k].cnt = cnt[i];
a[k].area = area[i];
k++;
}
}
qsort(a, k, sizeof(home), cmp);
printf("%d\n", k);
for (int i = 0; i < k; i++)
printf("%04d %d %.3f %.3f\n", a[i].id, a[i].num, 1.0 * a[i].cnt / a[i].num, 1.0 * a[i].area / a[i].num);
return 0;
}
7-7 最短工期 (25 分)
一个项目由若干个任务组成,任务之间有先后依赖顺序。项目经理需要设置一系列里程碑,在每个里程碑节点处检查任务的完成情况,并启动后续的任务。现给定一个项目中各个任务之间的关系,请你计算出这个项目的最早完工时间。
输入格式:
首先第一行给出两个正整数:项目里程碑的数量 N(≤100)和任务总数 M。这里的里程碑从 0 到 N−1 编号。随后 M 行,每行给出一项任务的描述,格式为“任务起始里程碑 任务结束里程碑 工作时长”,三个数字均为非负整数,以空格分隔。
输出格式:
如果整个项目的安排是合理可行的,在一行中输出最早完工时间;否则输出"Impossible"。
输入样例 1:
9 12
0 1 6
0 2 4
0 3 5
1 4 1
2 4 1
3 5 2
5 4 0
4 6 9
4 7 7
5 7 4
6 8 2
7 8 4
输出样例 1:
18
输入样例 2:
4 5
0 1 1
0 2 2
2 1 3
1 3 4
3 2 5
输出样例 2:
Impossible
Code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define N 105
int n, m, cnt;
int dis[N], ru[N], a[N][N];
int q[N];
int front = -1, rear = -1;
void topo()
{
while (rear > front)
{
int u = q[++front];
cnt++;
for (int i = 0; i < n; i++)
{
if (a[u][i] != -1)
{
if (--ru[i] == 0)
q[++rear] = i;
if (dis[i] < dis[u] + a[u][i])
dis[i] = dis[u] + a[u][i];
}
}
}
}
int main()
{
memset(a, -1, sizeof(a));
scanf("%d %d", &n, &m);
for (int i = 0; i < m; i++)
{
int u, v, w;
scanf("%d %d %d", &u, &v, &w);
a[u][v] = w;
ru[v]++;
}
for (int i = 0; i < n; i++)
{
if (ru[i] == 0)
q[++rear] = i;
}
topo();
int ans = 0;
for (int i = 0; i < n; i++)
{
if (dis[i] > ans)
ans = dis[i];
}
if (cnt == n)
printf("%d\n", ans);
else
printf("Impossible\n");
return 0;
}
7-8 路径判断 (20 分)
给定一个有N个顶点和E条边的无向图,请判断给定的两个顶点之间是否有路径存在。 假设顶点从0到N−1编号。
输入格式:
输入第1行给出2个整数N(0<N≤10)和E,分别是图的顶点数和边数。
随后E行,每行给出一条边的两个端点。每行中的数字之间用1空格分隔。
最后一行给出两个顶点编号i,j(0≤i,j<N),i和j之间用空格分隔。
输出格式:
如果i和j之间存在路径,则输出"There is a path between i and j.",
否则输出"There is no path between i and j."。
输入样例1:
7 6
0 1
2 3
1 4
0 2
1 3
5 6
0 3
输出样例1:
There is a path between 0 and 3.
输入样例2:
7 6
0 1
2 3
1 4
0 2
1 3
5 6
0 6
输出样例2:
There is no path between 0 and 6.
Code:
#include <stdio.h>
#include <stdlib.h>
int fa[15];
int Find(int x)
{
return (x == fa[x]) ? (x) : (fa[x] = Find(fa[x]));
}
void Union(int x, int y)
{
fa[Find(x)] = Find(y);
}
int main()
{
int n, m;
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; i++)
fa[i] = i;
while (m--)
{
int u, v;
scanf("%d %d", &u, &v);
Union(u, v);
}
int p, q;
scanf("%d %d", &p, &q);
if (Find(p) == Find(q))
printf("There is a path between %d and %d.\n", p, q);
else
printf("There is no path between %d and %d.\n", p, q);
return 0;
}
7-9 最短路径 (20 分)
给定一个有N个顶点和E条边的无向图,顶点从0到N−1编号。请判断给定的两个顶点之间是否有路径存在。如果存在,给出最短路径长度。 这里定义顶点到自身的最短路径长度为0。 进行搜索时,假设我们总是从编号最小的顶点出发,按编号递增的顺序访问邻接点。
输入格式:
输入第1行给出2个整数N(0<N≤10)和E,分别是图的顶点数和边数。 随后E行,每行给出一条边的两个顶点。每行中的数字之间用1空格分隔。 最后一行给出两个顶点编号i,j(0≤i,j<N),i和j之间用空格分隔。
输出格式:
如果i和j之间存在路径,则输出"The length of the shortest path between i and j is X.",X为最短路径长度, 否则输出"There is no path between i and j."。
输入样例1:
7 6
0 1
2 3
1 4
0 2
1 3
5 6
0 3
输出样例1:
The length of the shortest path between 0 and 3 is 2.
输入样例2:
7 6
0 1
2 3
1 4
0 2
1 3
5 6
0 6
输出样例2:
There is no path between 0 and 6.
Code:
#include <stdio.h>
#include <stdlib.h>
int a[15][15], vis[15];
int step[15], n;
int BFS(int x, int y)
{
int q[15];
int front = -1, rear = -1;
vis[x] = 1;
q[++rear] = x;
while (rear > front)
{
int tx = q[++front];
if (tx == y)
return step[y];
for (int i = 0; i < n; i++)
{
if (a[tx][i] && !vis[i])
{
vis[i] = 1;
step[i] = step[tx] + 1;
q[++rear] = i;
}
}
}
return -1;
}
int main()
{
int m;
scanf("%d %d", &n, &m);
while (m--)
{
int u, v;
scanf("%d %d", &u, &v);
a[u][v] = 1;
a[v][u] = 1;
}
int x, y;
scanf("%d %d", &x, &y);
int ans = BFS(x, y);
if (ans == -1)
printf("There is no path between %d and %d.", x, y);
else
printf("The length of the shortest path between %d and %d is %d.", x, y, ans);
return 0;
}
7-12 Dijkstra算法(模板) (30 分)
给一个n(1 ≤ n ≤ 2500) 个点 m(1 ≤ m ≤ 6200) 条边的无向图,求 s 到 t 的最短路。
输入格式:
第一行四个由空格隔开的整数 n、m、s、t。
之后的 m 行,每行三个正整数 si、ti、wi(1≤wi≤109),表示一条从si 到 ti 长度为 wi 的边。
输出格式:
一个整数,表示从s 到t 的最短路径长度。数据保证至少存在一条道路。
输入样例:
7 11 5 4
2 4 2
1 4 3
7 2 2
3 4 3
5 7 5
7 3 3
6 1 1
6 3 4
2 4 3
5 6 3
7 2 1
输出样例:
7
Code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define N 2505
#define INF 0x3f3f3f3f
int n, m, s, t, u, min;
int dis[N], a[N][N];
int vis[N];
void dij()
{
memset(dis, 0x3f, sizeof(dis));
dis[s] = 0;
for (int i = 1; i <= n; i++)
{
min = INF;
for (int j = 1; j <= n; j++)
{
if (!vis[j] && dis[j] < min)
{
min = dis[j];
u = j;
}
}
vis[u] = 1;
for (int j = 1; j <= n; j++)
{
if (dis[j] > dis[u] + a[u][j])
dis[j] = dis[u] + a[u][j];
}
}
}
int main()
{
memset(a, 0x3f, sizeof(a));
scanf("%d %d %d %d", &n, &m, &s, &t);
for (int i = 0; i < m; i++)
{
int u, v, w;
scanf("%d %d %d", &u, &v, &w);
if (w < a[u][v])
{
a[u][v] = w;
a[v][u] = w;
}
}
dij();
printf("%d\n", dis[t]);
return 0;
}