floyd
原理:
由上一层状态更新,可以去掉一维
应用:
- 求任意两点间最短距离
- 传递闭包问题(本人没认真学学校的离散课程)
- 找最小环(区别spfa:spfa是找负环,floyd是找权值最小的环——正权边)
- 恰好经过k条边的最短路(倍增的思想——不是很了解)
题单:
关键:直径最大的牧场的直径尽可能小
思考:
(1)答案组成
- 关于为什么大于等于连通块直径的最大值?
- 想象一个连通牧场里面还有一个连通牧场,根据题意,显然两个牧场没有连通的边,那一定是外边的牧场的直径作为新牧场的直径
- 经过新边的最长路径
(2)存储结构
//敲的第一遍
//未理解题意敲的
//没理解牧区的位置和邻接矩阵的关系
//woc,发现是先给牧区的位置坐标,然后用邻接矩阵描述两个牧区之间的连通关系
#include<bits/stdc++.h>
using namespace std;
const int N = 150,INF=0x3f3f3f3f;
int g[N][N];
double d[N][N],maxd[i];
char vtex[N][N];
int n;
void floyd(){
memset(d,0x3f,sizeof d);
for(int k=0;k<n;k++){
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
}
}
}
}
double solve(){
for(int i=0;i<n*n;i++){
for(int j=0;j<n*n;j++){
if(d[i][j]!=INF){
maxd[i]=max(maxd[i],d[i][j]);
}
}
}
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
if(d[i][j]==INF)
}
}
}
signed main(){
cin>>n;
memset(g,0x3f,sizeof g)l;
for(int i=0;i<n;i++){
int x,y;
cin>>x>>y;
g[x][y]=g[y][x]=1;
}
for(int i=0;i<n;i++){
cin>>g[i];
}
floyd();
cout<<solve()<<endl;
return 0;
}
//敲的第二遍
//我需要去存一个牧区的位置信息
#include<bits/stdc++.h>
#define x first
#define y second
using namespace std;
typedef pair<double,double> PDD;
const int N=150;
const double INF=1e20;
char g[N][N];
//maxd[i]存的是一个连通块中一个点到其他点最短距离的最大值
double d[N][N],maxd[N];
PDD vex[N];
int n;
double qdist(int i,int j){
double dx=vex[i].x-vex[j].x;
double dy=vex[i].y-vex[j].y;
return sqrt(dx*dx+dy*dy);
}
void floyd(){
for(int k=0;k<n;k++){
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
}
}
}
}
double solve(){
//res去存最终通过比较不同点在不同连通块的最大直径
double res=0; //求最大则res等于最小
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
if(d[i][j]<INF){
maxd[i]=max(maxd[i],d[i][j]);
res=max(maxd[i],res);
}
}
}
double res1=INF;
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
if(d[i][j]>=INF){
double dist=qdist(i,j);
res1=min(res1,dist+maxd[i]+maxd[j]);
}
}
}
//疑问:
//如果求得若干牧场的中最大直径的牧场,嵌套的牧场其实还是外面的牧场的距离吗
//想法:
//我认为是这样的,因为在solve的第二个大循环里,是取最小值,如果嵌套类型还是会取外面牧场的直径
return max(res,res1);
}
signed main(){
cin>>n;
//memset(d,0x3f,sizeof d);
//i==j时是同一个点,需要等于0
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
if(i!=j) d[i][j]=INF;
else d[i][j]=0;
}
}
for(int i=0;i<n;i++){
cin>>vex[i].x>>vex[i].y;
}
for(int i=0;i<n;i++){
cin>>g[i];
}
//在处理两个牧区之间距离的时候
//由于是对称矩阵
//我们只需要处理对称的部分即可
//由于双向边,所以上面三句错误
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
if(g[i][j]=='1'){
d[i][j]=qdist(i,j);
}
}
}
floyd();
printf("%.6lf\n",solve());
return 0;
}
传递闭包:
- 所有能间接到的点直接连接
- 与floyd关系
- floyd能通过 n 3 n^3 n3的时间复杂度求得
- 用邻接矩阵存储
- 步骤:
- 初始化(无权边)
- 三重循环(更新一个条件—— d [ i ] [ k ] d[i][k] d[i][k]和 d [ j ] [ k ] d[j][k] d[j][k]存在的话,更新 d [ i ] [ j ] = 1 d[i][j]=1 d[i][j]=1——传递闭包的一个概念)
- 与dp关系:
- 和上一题的联系一致
思考:
- 如何排序:
- 如何确定某个点时最小的
- 改进:
- 添加一条边只需要 n 2 n^2 n2 的复杂度即可完成,不需要每次添加就floyd一次(增量算法)
- 只判断(a,x)和(y,b)不能出效果,因为一条边的关系的加入需要精准传递到每一个点,(a,b)要建立联系,还需要判断,(a,y)和(x,b)
- 可以不用floyd的原因,因为传递闭包中一条边只会影响和他相连两个点的关系,不相连的点的关系也做判断只会浪费时间,只会最多影响 n 2 n^2 n2条边(左边n,右边n)
#include<bits/stdc++.h>
using namespace std;
int n,m;
const int N=30,M=N*N;
int g[N][N],st[N],d[N][N];
int check(){
for(int i=0;i<n;i++){
if(g[i][i]) return 2;
}
for(int i=0;i<n;i++){
for(int j=0;j<i;j++){
if((!g[i][j]&&!g[j][i])){
return 0;
}
}
}
return 1;
}
void get_min(){
for(int i=0;i<n;i++){
if(!st[i]){
bool flag=1;
for(int j=0;j<n;j++){
if(!st[j]&&g[j][i]){
flag=0;
break;
}
}
if(flag){
st[i]=1;
printf("%c",'A'+i);
}
}
}
}
signed main(){
while(cin>>n>>m,n||m){
memset(g,0,sizeof g);
int die,type=0;
for(int t=1;t<=m;t++){
char shi[5];
cin>>shi;
int a=shi[0]-'A',b=shi[2]-'A';
if(!type){
//当没有找到关系或者矛盾时才加边
g[a][b]=1;
for(int i=0;i<n;i++){
if(g[i][a]) g[i][b]=1;
if(g[b][i]) g[a][i]=1;
for(int j=0;j<n;j++){
if(g[i][a]&&g[b][j]){
g[i][j]=1;
}
}
}
type=check();
if(type) die=t;
}
}
if(type==1){
memset(st,0,sizeof st);
printf("Sorted sequence determined after %d relations: ",die);
for(int i=0;i<n;i++) get_min();
puts(".");
}
else if(type==2){
printf("Inconsistency found after %d relations.\n",die);
}
else if(!type){
puts("Sorted sequence cannot be determined.");
}
}
return 0;
}
思考:
- 怎么记录路径
- 怎么去分类
- 枚举中间点,找到以k为最大编号通过枚举i,j找到所有的环后就可以确定以k为最大编号的构成的环中的最小环
- 如何做到不重不漏的
- 因为floyd算法中,枚举到第k层时,d[i,j]在未更新前是不包含第k个点的,只有 1 ~ k − 1 1~k-1 1~k−1这些点,所以在1~k这些点中找到的环是不包含重(chóng)点的,
- 为什么要枚举点的最大编号,如果枚举i,j时i,j不是小于k的编号能否达到一样的效果?
- 枚举到n时也是对的,但是是冗余的,因为在做floyd算法时,最外层时枚举k,当到某一个k时,我们所能确定的是1~k-1点之间的最短路,就算此时我们找1~n中除了k的点i,j,
也只有枚举的i,j时小于k的点才会更新,大于k的点也只是提前多一层更新,至于是不是最短路更新的,之有到k = = == ==n时才回显现,所以如果是在1~k-1枚举i,j,反而可以减少没必要的中间更新
#include<bits/stdc++.h>
//#define int long long
using namespace std;
const int N=110,M=2e4+10,INF=0x3f3f3f3f;
int g[N][N],d[N][N];
int n,m;
int path[N],cnt,pos[N][N];
void get_path(int i,int j){
if(!pos[i][j])return ;
int k=pos[i][j];
get_path(i,k);
path[cnt++]=k;
get_path(k,j);
}
void dfloyd(){
memcpy(d,g,sizeof g);
int res=INF;
for(int k=1;k<=n;k++){
for(int i=1;i<k;i++){
for(int j=1;j<i;j++){
//(d[i][j]+g[i][k]+g[k][j])和一个加 longlong不一样
//括起来里面已经爆了
if((long long)d[i][j]+g[i][k]+g[k][j]<res){
res=(d[i][j]+g[i][k]+g[k][j]);
cnt=0;
path[cnt++]=k;
path[cnt++]=i;
get_path(i,j);
path[cnt++]=j;
}
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(d[i][j]>d[i][k]+d[k][j]){
d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
pos[i][j]=k; //记录最短路被更新时的中间节点
//通过递归一定能递归到两个点之间的边
}
}
}
}
if(res==INF){
puts("No solution.");
}
else{
for(int i=0;i<cnt;i++){
printf("%d ",path[i]);
}
}
}
signed main(){
cin>>n>>m;
memset(g,0x3f,sizeof g);
for(int i=1;i<=n;i++){
g[i][i]=0;
}
for(int i=0;i<m;i++){
int x,y,z;
cin>>x>>y>>z;
g[x][y]=g[y][x]=min(g[x][y],z);
}
dfloyd();
return 0;
}
思考:
- 第一反应是bellman-ford算法
- Bellman_ford算法和floyd在此题上的区别是什么
- 是恰好和不超过的区别吗
听y话:
(1) 原始 d [ k , i , j ] d[k,i,j] d[k,i,j]含义:表示从i到j只经过1~k的话,最短路径是多少
(2) 本题含义:表示从i到j,恰好经过k条边的最短距离
状态转移:
- 类floyd算法,但是并不更新直接更新 g [ i ] [ j ] g[i][j] g[i][j],用 r e s [ i ] [ j ] res[i][j] res[i][j]去存储结果, g [ i ] [ j ] g[i][j] g[i][j]作为一个单位元去累加(也包含类快速幂思想)
#include<bits/stdc++.h>
using namespace std;
int n,t,m,s,e;
const int N=1000;
int g[N][N],res[N][N];
map<int,int> idx; //离散化数组
void mul(int c[][N],int a[][N],int b[][N]){
static int temp[N][N];
memset(temp,0x3f,sizeof temp);
for(int k=0;k<n;k++){
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
temp[i][j]=min(temp[i][j],a[i][k]+b[k][j]);
}
}
}
memcpy(c,temp,sizeof temp);
}
void qui(int k){
memset(res,0x3f,sizeof res);
for(int i=0;i<n;i++) res[i][i]=0;
while(k){
if(k&1) mul(res,res,g);
k>>=1;
mul(g,g,g);
}
}
signed main(){
cin>>t>>m>>s>>e;
if(!idx.count(s)) idx[s]=n++;
if(!idx.count(e)) idx[e]=n++;
memset(g,0x3f,sizeof g);
for(int i=0;i<m;i++){
int a,b,c;
cin>>c>>a>>b;
if(!idx.count(a)) idx[a]=n++;
if(!idx.count(b)) idx[b]=n++;
a=idx[a],b=idx[b];
g[a][b]=g[b][a]=min(g[a][b],c);
}
qui(t);
cout<<res[idx[s]][idx[e]]<<endl;
return 0;
}