水水的floyd算法

最短路问题模板题

平面上有n个点(n<=100),每个点的坐标均在-10000~10000之间。

其中的一些点之间有连线。若有连线,则表示可从一个点到达另一个点,即两点间有通路,通路的距离为两点间的直线距离。

现在的任务是找出从一点到另一点之间的最短路径。

输入格式
共n+m+3行,其中:
第一行为整数n。
第2行到第n+1行(共n行),每行两个整数x和y,描述了一个点的坐标。
第n+2行为一个整数m,表示图中连线的个数。
此后的m行,每行描述一条连线,由两个整数i和j组成,表示第i个点和第j个点之间有连线。
最后一行:两个整数s和t,分别表示源点和目标点。

输出格式
仅一行,一个实数(保留两位小数),表示从s到t的最短路径长度。

样例
输入数据 1
5
0 0
2 0
2 2
0 2
3 1
5
1 2
1 3
1 4
2 5
3 5
1 5
输出数据 1
3.41

 

#include<bits/stdc++.h>
using namespace std;
int n,m,a[105][3],x,y,s,t;
double f[105][105];
int main(){
    cin>>n;
    for(int i=1;i<=n;i++)
	     cin>>a[i][1]>>a[i][2];
    cin>>m;
    memset(f,0x7f,sizeof(f));
    for(int i=1;i<=n;i++)
	    f[i][i]=0;
    for(int i=1;i<=m;i++)
	{
        cin>>x>>y;
        f[x][y]=f[y][x]=sqrt(pow(a[x][1]-a[y][1],2)+pow(a[x][2]-a[y][2],2));
    }
    cin>>s>>t;
    for(int k=1;k<=n;k++)
    for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++)
    if (k!=i&&k!=j&&i!=j)
            if(f[i][k]+f[k][j]<f[i][j])
                f[i][j]=f[i][k]+f[k][j];
    printf("%.2lf",f[s][t]);
    return 0;
}

  

 

 

 

最佳牧场

约翰拥有P(1<=P<=500)个牧场.

贝茜特别喜欢其中的F个.所有的牧场 由C(1 < C<=8000)条双向路连接,第i路连接着ai,bi,需要Ti(1<=Ti< 892)单 位时间来通过.

作为一只总想优化自己生活方式的奶牛,贝茜喜欢自己某一天醒来,到达所有那F个她喜欢的 牧场的平均需时最小.那她前一天应该睡在哪个牧场呢?请帮助贝茜找到这个最佳牧场.

img

Format
Input
第1行输入三个整数P,F C.

之后F行每行输入一个整数表示一个贝茜喜欢的牧场.

之后C行每行输入三个整数ai,bi,Ti,描述一条路.

Output
一个整数,满足题目要求的最佳牧场.如果有多个答案,输出编号最小的

Samples
输入数据 1
13 6 15
11
13
10
12
8
1
2 4 3
7 11 3
10 11 1
4 13 3
9 10 3
2 3 2
3 5 4
5 9 2
6 7 6
5 6 1
1 2 4
4 5 3
11 12 3
6 10 1
7 8 7

 

sol

在这个代码中,有两处标红的地方

如果两个都不加的话,则会出错

如果两个都加,或只加其中一个均无没问题。

原因在于

 在出现上面这种环,则会跑出a自己到自己的距离为9,这个值是小于从前设定的a自己到自己为无限大的值

于是a自己到自己就为9了,这个与实际是不相符合的。

如果加上

 for(int i=1;i<=n;i++)
        g[i][i]=0;
则不会更新自环的边

如果加上
if (k!=i&&k!=j&&i!=j)
也不会来算自环的值,后面因为又加了两者之间距离不能为无限大,所以也是对的。

总结:图的初始化设置是非常重要的。


 

#include<bits/stdc++.h>
using namespace std;
int g[510][510],n,f,m,l[510],u,v,w,ans,mn=INT_MAX;
int main()
{
    scanf("%d%d%d",&n,&f,&m);
    //n个点,m边条, 
    memset(g,63,sizeof(g));
    int inf=g[1][1];
    for(int i=1;i<=f;i++)
        scanf("%d",&l[i]);
    for(int i=1;i<=m;i++)
        scanf("%d%d%d",&u,&v,&w),g[u][v]=g[v][u]=w;
    for(int i=1;i<=n;i++)
        g[i][i]=0;
    for(int k=1;k<=n;k++)
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
            if (k!=i&&k!=j&&i!=j)
                if(g[i][k]+g[k][j]<g[i][j])
                    g[i][j]=g[i][k]+g[k][j];
    for(int i=1;i<=n;i++)
    {
        int sum=0;
        for(int j=1;j<=f;j++)
            if(g[i][l[j]]!=inf)
			    sum+=g[i][l[j]];
        if(sum<mn)
            mn=sum,ans=i;
    }
    printf("%d",ans);
}

  

 

 

P12331. Cow Contest奶牛的比赛

Description
FJ的N(1 <= N <= 100)头奶牛们最近参加了场程序设计竞赛:)。在赛场上,奶牛们按1..N依次编号。每头奶牛的编程能力不尽相同,并且没有哪两头奶牛的水平不相上下,也就是说,奶牛们的编程能力有明确的排名。

整个比赛被分成了若干轮,每一轮是两头指定编号的奶牛的对决。

如果编号为A的奶牛的编程能力强于编号为B的奶牛(1 <= A <= N; 1 <= B <= N; A != B) ,那么她们的对决中,编号为A的奶牛总是能胜出。

FJ想知道奶牛们编程能力的具体排名,于是他找来了奶牛们所有 M(1 <= M <= 4,500)轮比赛的结果,希望你能根据这些信息,推断出尽可能多的奶牛的编程能力排名。

比赛结果保证不会自相矛盾。

Format
Input
第1行: 2个用空格隔开的整数:N 和 M

第2..M+1行: 每行为2个用空格隔开的整数A、B,描述了参加某一轮比赛的奶 牛的编号,以及结果

(编号为A,即为每行的第一个数的奶牛为 胜者)

Output
第1行: 输出1个整数,表示排名可以确定的奶牛的数目
Samples
输入数据 1
5 5
4 3
4 2
3 2
1 2
2 5
输出数据 1
2
Hint

编号为2的奶牛输给了编号为1、3、4的奶牛,也就是说她的水平比这3头奶牛都差。

而编号为5的奶牛又输在了她的手下,也就是说,她的水平比编号为5的奶牛强一些。

于是,编号为2的奶牛的排名必然为第4,编号为5的奶牛的水平必然最差。其他3头奶牛的排名仍无法确定

 

#include<bits/stdc++.h>
using namespace std;
int n,m,fr,ta,f[505][505],ans;
int main()
{
    cin>>n>>m;
    for(int i=1,x,y;i<=m;i++)
	     cin>>x>>y,f[x][y]=1;
    for(int k=1;k<=n;k++)
    for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++)
        if (k!=i&&k!=j&&i!=j)
            if(f[i][k]&&f[k][j])
                f[i][j]=1;
    for(int i=1;i<=n;i++)
	{
		int sum=0;
        for(int j=1;j<=n;j++)
	    	if(f[i][j]||f[j][i])sum++;
		if(sum==n-1)ans++;
	}
    cout<<ans; 
    return 0;
}

  

 

P12332. 最短路上的统计
ID: 2079
传统题
1000ms
256MiB
尝试: 84
已通过: 26
难度: 6
上传者:

管理员 (root)
标签>
Description
一个无向图上,没有自环,所有边的权值均为1,对于一个点对(a,b) 我们要把所有a与b之间所有最短路上的点的总个数输出。

Format
Input
第一行n,m,表示n个点,m条边

接下来m行,每行两个数a,b,表示a,b之间有条边

在下来一个数p,表示问题的个数

接下来p行,每行两个数a,b,表示询问a,b

n<=100,p<=5000

Output
对于每个询问,输出一个数c,表示a,b之间最短路上点的总个数

Samples
输入数据 1
5 6
1 2
1 3
2 3
2 4
3 5
4 5
3
2 5
5 1
2 4
输出数据 1
4
3
2

Hint

对于第一个询问有2,3,4,5这四个点

 

#include<bits/stdc++.h>
using namespace std;
int n,m,p,dis[1005][1005];
int x,y;
int main()
{
	cin>>n>>m;
	memset(dis,0x3f,sizeof(dis));
	for(int i=1;i<=m;i++)
	{
		cin>>x>>y;
		dis[x][y]=dis[y][x]=1;
	}
	for (int i=1;i<=n;i++)
	     dis[i][i]=0;
	for(int k=1;k<=n;k++)
		for(int x=1;x<=n;x++)
			for(int y=1;y<=n;y++)
				dis[x][y]=min(dis[x][y],dis[x][k]+dis[k][y]);
    cin>>p;
	for(int i=1;i<=p;i++)
	{
		int l,r,ans=0;
        cin>>l>>r;
        for(int j=1;j<=n;j++)
            if(dis[l][j]+dis[r][j]==dis[l][r])
                ans++;
        cout<<ans<<endl;
	}
	return 0; 
}

  

 

 

P12364. 删边操作1

Description
给定一个N节点无向连通图,问最多可以删除多少条边,使得删除后的图,对于两个节点 U,V其最短路没有发生改变。

图中没有自环,没有重边

Format
Input
第一行给出N,M,分别代表点与边

接下来M行,描述这个图

N<=300

Output
如题

Samples
输入数据 1
3 3
1 2 2
2 3 3
1 3 6
输出数据 1
1

 如上图,本来1与3之间有边为9

但却找到了中间点2,更新了1到3的最短路

于是1到3的边,是可以不要的

 

#include <bits/stdc++.h>
using namespace std;

int n, m, ans;
int dis[305][305];//最短路数组
struct edge {
	int u, v, w;
} e[100005];

int main() {
	cin >> n >> m;
	memset(dis, 0x3f, sizeof(dis));
	for (int i = 1, u, v, w; i <= m; i++) {
		cin >> u >> v >> w;
		e[i].u = u, e[i].v = v, e[i].w = w;
		dis[u][v] = dis[v][u] = w;
	}
	for (int i = 1; i <= n; i++)
		dis[i][i] = 0;
	for (int k = 1; k <= n; k++) 
		for (int i = 1; i <= n; i++) 
			for (int j = 1; j <= n; j++) 
			dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);//Floyd
	for (int i = 1; i <= m; i++) 
	//枚举所有的边 
	{
		int flag = 0;
		for (int j = 1; j <= n; j++) 
		//枚举中间点 
		{
			if (j != e[i].u && j != e[i].v)
			if (dis[e[i].u][e[i].v] == dis[e[i].u][j] + dis[j][e[i].v])
			//对于e[i].u与e[i].v这两个点来说
			//它们本是一条边上的两个点
			//现在它们之间的最短路,居然找到了一个中间点来更新来最短路
			//说明它们两者之间从前的边,是可以去掉的 
			    flag = 1;
		}
		ans += flag;
	}
	cout << ans;
	return 0;
}

  

posted @ 2024-04-19 11:56  我微笑不代表我快乐  阅读(0)  评论(0编辑  收藏  举报