加载中...

图的简单入门

debug一天可能是括号的问题。。

推荐使用邻接矩阵存储点少的

开个二维数组[i][j]
i存放入边

推荐使用邻接表点多的存储边 使用vector

先定义一个结构体

node{
int v;//终点编号
int w;//边权重
}

这样vector v[N];//就可以很好的表示出来了
加入一个node

node temp;
temp.v=3;
temp.w=4;
v[1].push_back(temp);//这样就插入了一个函数
当然使用c++的类构造函数也是可以
struct Node{
int v,w;
Node(int _v,int _w):v(_v),w(_w){}//这样构造函数
直接v.push_back(Node(3,4));
}
for(int i=1;i<=m;i++){
int u,v,l;
cin>>u>>v>>l;
p[u].push_back((edge){v,l})}



遍历图;

int n,G[max][max]
bool vis[max]={0}
void DFStrave(){
  for(int u=0;u<allpeople;u++){//对所有n个顶点进行操作
      if(graph[nowvisit][u]>0)//如果能到达,有的时候还得让graph[nowvisit][u]=graph[u][nowvist]=0防止回头
      
      if(vis[u]==false)//如果没有遍历过 进行遍历
      //此时一般还会定义一些变量达到对每个连通块**收集信息**效果,
      
      DFS(u,1);//dfs加入了其他的变量参数
      //dfs出来后,这些变量就会变成成为**这个连通块**的信息了,使用一些if语句存放处理这个变量
  }

}
void dfs(int u,int depth){
vis[u]=true;
for(int v=0;v<=n;v++)//n是顶点数
  if(vis[v]==false&&G[u][v]!=INF){//没有访问过并且能从u到达v
    DFS(i,depth+1);
  }
}

图论,第一步考虑怎么样构建表

1.是否需要将名字转化为编号 int change(string str)//变成了数字才好处理
数据小的时候
map<string ,int >stringtoint //使用map函数来储存,但是如果数据大得用hash
map<int ,string >inttostring
int change (string str){
	if(stringtoint.find(str)!=stringtoint.end()){//找一下发现有这个str的记录,返回数字
		return map[str];
	}
	else {
		stringtoint[str]=numperson;//新建这个记录等于现在的人数
		inttostring[numperson]=str;//两个map都要
		return numperson++;//开始初始化的时候设置了numperson为0,为了数组下标考虑,
	}
}
数据大的时候

int getid(){

}

  2.图的信息点权边权如何输入
    是否需要因为指向方向的变化而改变(邻接表)
   const int maxn=2010;//最大点数
int G[maxn][maxn]={0};//边权 
int weigh[maxn]={0};//点权 
for(int i=0;i<n;i++){
	cin>>str1>>str2>>w;
	int id1=change(str1);
	int id2=change(str2) ;
	weigh[id1]+=w;
	weigh[id2]+=w;
	G[id1][id2]+=w;
	G[id2][id1]+=w;
	 
} 

遍历的时候是否已经是一块连通图,是否只能访问一次

有没有遍历层数的限制(有的话,选用bfs因为dfs可能会导致一次转发次数被重复计算,没有选择dfs)

简单不带权的搜索练习

考题1:连通块计数问题 一般会不给出权值,主要考察普通的搜索

#include<bits/stdc++.h>
using namespace std;
const int N=1005;
vector<int> G[N];//邻接表存边, 
bool vis[1005];
int currentpoint;
void dfs(int v){
	if(v==currentpoint)//终止条件,下一个删除的点是顶点的时候 
	return;//
	vis[v]=true;
	for(int i=0;i<G[v].size();i++){
		if(vis[G[v][i]]!=true)
		{
			dfs(G[v][i]);
		}
	} 	
}
int main(){
    int n,m,k;
    cin>>n>>m>>k;
    int x,y;
    for(int i=0;i<m;i++){
    	cin>>x>>y;
		G[y].push_back(x);//无向图,两个方向都要 
		G[x].push_back(y);  
	}
	for(int i=0;i<k;i++){
		cin>>currentpoint;
		memset(vis,false,sizeof(vis));
		int block=0;
		for(int i=1;i<=n;i++){//从编号1的点开始搜索到n 
			if(i!=currentpoint&&!vis[i])//这个点没被删除而且没经过这个点 
			//上面这里必须要有否则进入了dfs就连通块+1了 
			{ 	dfs(i);
				block++;  
			}
		}
		cout<<block-1<<endl;		
	}
	
    return 0;
}

forwards on weibo 单向图+单连通块+计算层数(使用node存编号和层数)+对成环的处理(只能转发一次)

#include<bits/stdc++.h>
using namespace std;
const int N=1005;
struct node {
	int id;
	int layer;

};
vector<int>graph[N];
bool vis[N];
queue<node> q;//单向图+单连通块+计算层数(使用node存编号和层数)+对成环的处理(只能转发一次)

int k,n;

int  bfs(int s) {
	int nums=0;
	node first;//使用node
	first.id=s;
	first.layer=0;
	queue<node>q;
	q.push(first);
	vis[first.id]=true;
	while(!q.empty()) {
		node top=q.front();
		q.pop();
		for(int i=0; i<graph[top.id].size(); i++) {
			node next;
			next.id=graph[top.id][i];//使用node
			next.layer=top.layer+1;
			if(vis[next.id]==false&&next.layer<=k) {
				q.push(next);
				nums++;
				vis[next.id]=true;				
			}
		}
	}
	return nums;
}

int main() {
	cin>>n>>k;
	for(int i=1; i<=n; i++) {
		int person;
		int cnt;
		cin>>cnt;
		for(int j=0; j<cnt; j++) {
			cin>>person;
			graph[person].push_back(i);
		}
	}
	int ans,askperson;
	cin>>ans;
	for(int i=0; i<ans; i++) {
		cin>>askperson;
		memset(vis,false,sizeof(vis));
		cout<<bfs(askperson)<<endl;

	}

	return 0;
}

deepest root 单连通块,判断图是否能转化为树(并查集+n-1),无向图dfs(防止回去),保存最大值,找高度最深的根结点

思路
首先从任意节点出发dfs得到一个最深的叶结点集合A,然后从A集合中任意选一个结点出发dfs得到另一个最深的叶结点集合B,
也就是一开始我并不知道对于一幅图哪个顶点是他的最长跟端点,所以我随便我随便dfs一个点如下面b发现能到达的最长的叶子节点集合里有c和a叶子结点,那么再从c和a中挑一个dfs,就会得到c和e为叶子结点,所以结合a和集合b的交集,都可以作为最长的结点
c
|
a--b-d-e
下面这里第一次选择了g进行dfs获得集合里面有e,从集合任意选择一个即e,dfs找到f
c
|
a--b-d-e
|
g
|
f


#include<bits/stdc++.h>
using namespace std;
const int N=10005;
vector<int> graph[N];
int isfathher[N];
int root[N];
int findfathher(int x){
    int a=x;
    while(x!=isfathher[x]){
        x=isfathher[x];
    }
    while(a!=isfathher[a]){
        int z=a;
        isfathher[z]=x;
        a=isfathher[a];
         
    }return x;
}

void combine(int x,int y){
    int faa=findfathher(x);
    int fab=findfathher(y);
    if(faa!=fab){
        isfathher[faa]=fab;
    }
}
void init(int x){
    for(int i=1;i<=x;i++){
        isfathher[i]=i;
    }
}
int calblock(int n){
    int block=0;
    for(int i=1;i<=n;i++){
        root[findfathher(i)]=true;
        
    }
    for(int i=1;i<=n;i++){
        block+=root[i];
    }
    return block;
}
//上面全是并查集
int maxheight=0;
vector<int>an,temp;
void dfs(int height,int n,int pre){
    if(height>maxheight){
      temp.clear();
      temp.push_back(n);
       maxheight=height;
    }
    else if(height==maxheight){
        temp.push_back(n);
    }
    for(int i=0;i<graph[n].size();i++){
        if(graph[n][i]==pre) continue;//防止回去
        dfs(height+1,graph[n][i],n);
    }
}
int main(){
	int m;
    cin>>m;
    int x,y;init(m);
    for(int i=0;i<m-1;i++)
    {
        cin>>x>>y;
        graph[x].push_back(y);
        graph[y].push_back(x);
        combine(x,y);
    }
    
    int block=calblock(m);
    if(block!=1){
        printf("Error: %d components",block);
    } 
    else {
        dfs(1,1,-1);
        an=temp;
        dfs(1,an[0],-1);//取已经找出的根节点的任意个结点重新搜
        for(int i=0;i<temp.size();i++){
            an.push_back(temp[i]);//重新写
        }
        sort(an.begin(),an.end());
        cout<<an[0]<<endl;
        for(int i=1;i<an.size();i++){
            if(an[i]==an[i-1]) continue;
            cout<<an[i] <<endl;
        }
    }
	
	return 0;		
}

----------------------------------------------------------

简单带权的搜索练习

head of a gang 无向图+边权(还需要转成点权)+独立连通块的处理+字符串哈希

一开始以为是无向图,后来发现需要计算一组的边权最大的人,而这个数据必然你打给我,我打给你都要需要算上
一开始用的邻接表存图,发现需要加入点权,而自己又懒得用node结构体,所以改为邻接表

#include<bits/stdc++.h>
using namespace std;
const int N=2010;
 int n,k;
int numperson=0,t=0,totaltime=0;
map<string ,int >stringtoint;
map<int ,string >inttostring;
int graph[N][N];
int weigh[N];
bool vis[N];
int change(string a){
    if(stringtoint.find(a)!=stringtoint.end()){
        return stringtoint[a];
    } 
    //不用insert
    stringtoint[a]=numperson;//巧妙利用全局变量对人数进行计数
    inttostring[numperson]=a;
    
    
    numperson++;
    return numperson-1;
}
map<string,int>gang;//使用string是因为map,自动字典序排序
void dfs(int n,int &head,int &nummember,int &totalvalue){
    
    nummember++;
    vis[n]=true;
    
    
    if(weigh[n]>weigh[head]){
        head=n;
    } 
    for(int i=0;i<numperson;i++){
        if(graph[n][i]>0){//如果能到达
            totalvalue+=graph[n][i];//就算不能从那个点遍历但是还是在图里面所以还是得计算他的边权
            graph[n][i]=graph[i][n]=0;//防止重复计算
            if(vis[i]==false)//如果没有被访问过就需要进行访问
            dfs(i,head,nummember,totalvalue);
            
        }
    }
}
void dfstravel(){
    for(int i=0;i<numperson;i++){
        if(vis[i]==false){
            
            int head=i,nummember=0,totalvalue=0;
            dfs(i,head,nummember,totalvalue);
            
            if(nummember>2&&totalvalue>k){
                gang[inttostring[head]]=nummember;
            }
        }
    }
} 

int main(){
	int w;
    string a,b;
   
    cin>>n>>k;
    for(int i=0;i<n;i++){
        cin>>a>>b>>w;
        
        int id1=change(a);
        int id2=change(b);
        graph[id1][id2]+=w;
        graph[id2][id1]+=w;//录入图
        weigh[id1]+=w;
        weigh[id2]+=w;
        
    }
    dfstravel();
	cout<<gang.size()<<endl;
    map<string ,int >::iterator i;
    for(i=gang.begin();i!=gang.end();i++)
    cout<<i->first<<" "<<i->second<<endl;
    
	
	
	return 0;		
}



遍历图里面的点,找到入度为0的点就放入队列
找到入度为0的点,再去找以这个点能到达的点对到达他的入度-1;
map(点,还剩多少入度);
只有剩余为0的点才能进这个队列

最小生成树:让所有的点连在一起而且使得所有边的权值之和最小

k算法(需要并查集)稀疏图
先将图里面所有边的权值排序,由小到大一个个操作

看两侧是不是一个集合,
如果不是 一个集合,要这个边,联合起点和终点合在一起
如果是 跳过

跑无向图的话答案是对的 但是的话边集是少一侧的

p算法(指定哪一个点为起点都可以)稠密图
边解锁了才能走,可以考虑,在所有解锁的边(包括之前操作的不是本点的边)里面选最小的,如果这个边左右两侧没有新结点,就跳过
如果有新结点,放入点集set,解锁他的边

for循环为了 防止树
一个边的集合()
出发点放进set,
for(遍历所有的点){
如果这个点的边没有记录过
就放入这个边的记录

}
while(如果堆不为空){
弹出最小的边
获得这个堆中最小的边的点
如果这个点不在set中即就是没有选过新的点
set添加这个记录
答案增加这条边
然后放入这个点的所有边进入堆

缔结特斯拉(指定一个点告诉你这个图里面所有点的最短距离是多少,到达不了正无穷,权不能为负数的边(可能负值的环最短。。 ))
每次选择到原点最短的点走,走了后解锁从这个点的距离;
通过这个点发现更新的路,发现新的点出现会不会更新记录
使用过就不用,锁死
每一轮,如果 有更小的就更新
表from( 点,距离)
一开始只有一个点
迪杰斯特拉(G,d[],s){
初始化

for(循环n次每个点){
u=使d[u]最小的还未被访问的顶点的标号
记u已经被访问;
for(从u出发能到达的所有顶点v){
if(v未被访问&&以u为中介点使得s到顶点v的最短距离d[v]更优)
优化d[v];
}

}

}

赋初值INF使用fill函数(迭代器初,迭代器末,要填入的数字)
memset是对字节进行操作一般赋值0/1使用

一个表:

对一个点遍历他的所有to点 ,将所有的值记录答案,然后锁住这个点。
然后最这个答案中挑出一个最小的值,以这个点为起点,遍历他的to点,在这个点上加距离,如果比之前的答案记录小就更新这个记录

距离表,存放距离和点
一个哈希表 实现锁每次选择完放进去,set有这个记录就是锁了
遍历距离表 找到距离最小(这个距离最小指的是从源点出发)的而且之前没有操作过的点(哈希表查询),while(有这个点){
遍历他所有的边
找到下一个点tonode
if距离表没有关于tonode的记录
放入这个距离,这个点.距离+边的值
else有这个记录实施能不能更新
min()

遍历完加上这个点的记录
然后在哈希表放入插着记录
}

优化
(系统提供的堆,不能临时改变已经在堆里的值)
自己建造一个堆,能把在里面堆的值并且重新调整
堆需要的功能 update add ingnore(已经排除的结点应该忽略)
在堆上调整logn
怎么往上移动(交换和父亲)
弹出堆中最小的点,然后以这个点为桥梁点看他的to的记录 :如果之前记录没有,加add一个,如果之前有update,如果搞过(-1)就ignore

三个增加考点:
1.增加边权
2.增加点权
3.问多少最短路径多少条

emergency 独立的 点权,最短路数量 问题+

#include<bits/stdc++.h>
using namespace std;
const int N=510;
const int inf=1000000000;
int n,m,st,ed,graph[N][N],weigh[N];//weigh为各个点的点权,这一行记录一般图需要的东西,边权点权
int d[N],w[N],num[N];//w为最大点权之和,d[]为从这个起始点开始到每个点的距离,num表示表示道路的数量
bool vis[N]={false};

void dijkstra(int s){
    fill(d, d+N, inf);//一开始从s到所有的点都是无穷大
     //memset(num,0, sizeof(num));//所有需要记录的数据要么无穷大,要么设为0;
     //memset(w, 0, sizeof(w));//这里如果下面判断的是找最大值就需要设为0,如果找的是最小值就需要设置为inf
     d[s]=0;//处理s相关的点
     w[s]=weigh[s];//初始赋值s距离,s点权为0,s路径条数为1
     num[s]=1;
    
     for(int i=0;i<n;i++){
         int current=-1,min=inf;//最小值
         for(int j=0;j<n;j++){//第一个for从0到n遍历所有点找最小距离
             if(d[j]<min&&vis[j]==false){//如果有最小距离且没有走过,记录这个点
                 current=j;
                 min=d[j];
             }
         }
         if(current==-1) return ;//当前待处理的点还是-1,说明剩下的点都无法走,返回
         vis[current]=true;//注意选择是这里选择,发现了就立刻标记,下面只是更新
         //上面是找距离最小的点所以只需要关注d,下面是需要更新从current点能到的点的值需要使用边权所以用的graph
            for(int next=0;next<n;next++){//第二个for从0到n判断每个点是否能更新,注意是每一个点,而不是从当前点到达的某个点
             if(graph[current][next]!=inf){//能走话进行判断。。。 如果优化 可以加个条件 如果没有走过才进入下面在进行判断能否更新即vis[next]==false&&
                 
                 if(d[current]+graph[current][next]<d[next]){//说明前面的记录全部都要改变
                     d[next]=d[current]+graph[current][next];
                     w[next]=w[current]+weigh[next];
                     num[next]=num[current];
                 }
                 else if(d[current]+graph[current][next]==d[next]){
                     if(w[current]+weigh[next]>w[next])
                         w[next]=w[current]+weigh[next];
                 num[next]+=num[current];//多出来的道路是从新开发的点找到的点
                 }             
             }
         }
         
         
         
     }
}
int main(){
	cin>>n>>m>>st>>ed;
    for(int i=0;i<n;i++){
        cin>>weigh[i];
    }
    fill(graph[0],graph[0]+N*N, inf);
    for(int i=0;i<m;i++){
        int a,b,l;
        cin>>a>>b>>l;
        graph[a][b]=l;
        graph[b][a]=graph[a][b];
    }
    dijkstra(st);
    cout<<num[ed]<<" "<<w[ed];
	return 0;	
}

travel plan 需要pre记录前驱 并且与上面一题不同的是更新的是小的 所以需要初始话的时候要赋值inf

#include <bits/stdc++.h>
using namespace std;
const int inf=10000000;
const int N=510;

int n,m,st,ed;
int graph[N][N],weigh[N][N];
int d[N],w[N],pre[N];

bool vis[N];
void dijstra(int s){
        fill(w,w+N,inf);
           fill(d,d+N,inf);
    d[s]=0;
    w[s]=0;
    
    for(int i=0;i<n;i++){
        int current=-1,min=inf;
        for(int j=0;j<n;j++){
            if(min>d[j]&&vis[j]==false){
                min=d[j];
                current=j;
            }
        }
        if(current==-1) return ;
        vis[current]=true;
        for(int next=0;next<n;next++){
            if(graph[current][next]!=inf)
            if(d[current]+graph[current][next]<d[next]){
                d[next]=d[current]+graph[current][next];//距离
                w[next]=w[current]+weigh[current][next];//花费
                pre[next]=current;//更新前驱
            }
            else if(d[current]+graph[current][next]==d[next]){
                if(w[next]>w[current]+weigh[current][next])
                {w[next]=w[current]+weigh[current][next];
                    pre[next]=current;//更新前驱
                } 
            }       
        }
    }
}
void dfs(int s){//dfs搜索打印前驱,从ed放置
    if(s==st)//到了起点就return
    {    printf("%d ",st);
        return ;
    }    
    dfs(pre[s]);
    printf("%d ",s);//注意dfs和打印的顺序
    
}
int main(){
    cin>>n>>m>>st>>ed;
    fill(graph[0],graph[0]+N*N,inf);
    for(int i=0;i<m;i++){
        int a,b;
        cin>>a>>b;
        cin>>graph[a][b]>>weigh[a][b];
        graph[b][a]=graph[a][b];
        weigh[b][a]=weigh[a][b];
    }
    dijstra(st);
    
    dfs(ed);//放入了终点
 
    cout<<d[ed]<<" "<<w[ed];
}

然而一般都是通过dijkstra+dfs来解决最短路径问题的 核心找前驱

1.使dijkstra记录所有最短路径
2.dfs遍历所有最短的路径,找出以第二标尺为最优的路径
几个概念 多个pre形成的一个棵树,递归终点到了叶子结点就回收

#include <bits/stdc++.h>
using namespace std;
const int inf=10000000;
const int N=510;

int n,m,st,ed;
int graph[N][N],weigh[N][N];
int d[N],w[N];
vector<int>pre[N];//使用前缀数组保存
vector<int>path,temppath;//建立path,temppath存放某一条的路径
bool vis[N];//标记为真
void dijstra(int s){
        
           fill(d,d+N,inf);
    d[s]=0;
    w[s]=0;
    
    for(int i=0;i<n;i++){
        int current=-1,min=inf;
        for(int j=0;j<n;j++){
            if(min>d[j]&&vis[j]==false){
                min=d[j];
                current=j;
            }
        }
        if(current==-1) return ;
        vis[current]=true;
        for(int next=0;next<n;next++){
            if(graph[current][next]!=inf)
            if(d[current]+graph[current][next]<d[next]){
                d[next]=d[current]+graph[current][next];//距离
                pre[next].clear();//记得next之前的pre前缀记录都需要清空
            pre[next].push_back(current);
               
            }
            else if(d[current]+graph[current][next]==d[next]){
                
                    pre[next].push_back(current);
                
            }       
        }
    }
}
int mincost=inf;//定义一个全局的变量保存最优
void dfs(int s){
    temppath.push_back(s);//进来第一步先把结点存进去temppath,
    if(s==st)
    {   int cost=0;//定义一个暂时的变量进行处理
     for(int i=temppath.size()-1;i>0;i--){//数组从后往前,即路径从开始到后面,
         int current=temppath[i],next=temppath[i-1];
         cost+=weigh[current][next];
     
     }
     if(cost<mincost){//
         mincost=cost;
         path=temppath;
     }
        
     temppath.pop_back();//用完记得回溯
     return ;
    }
    
    for(int i=0;i<pre[s].size();i++){
        dfs(pre[s][i]);
    
    }
    temppath.pop_back();//用完记得回溯
   
    
}
int main(){
    cin>>n>>m>>st>>ed;
    fill(graph[0],graph[0]+N*N,inf);
    for(int i=0;i<m;i++){
        int a,b;
        cin>>a>>b;
        cin>>graph[a][b]>>weigh[a][b];
        graph[b][a]=graph[a][b];
        weigh[b][a]=weigh[a][b];
    }
    dijstra(st);
    
    dfs(ed);
     for(int i=path.size()-1;i>=0;i--){//倒着输出
         cout<<path[i]<<" ";
     }
    cout<<d[ed]<<" "<<mincost;
}

public bike management 无向图+dijkstra+dfs(不是简单相加)+次标尺平均值

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

const int inf=1e9;
const int N=510;

int n,m,cmax,ed,graph[N][N],weigh[N];
int d[N],minneed=inf,minremain=inf;//由题目提示直到需要一个全局变量存储最少解
bool vis[N]={false};
vector<int >pre[N];//
vector<int >path,temppath;

void dijstra(int s){
    fill(d,d+N,inf);
    d[s]=0;
    for(int i=0;i<n;i++){
        int u=-1,min=inf;
        for(int j=0;j<=n;j++){
            if(d[j]<min&&vis[j]==false){
                u=j;
                min=d[j];
            }
        }
        if(u==-1) return ;
        vis[u]=true;
        for(int v=0;v<=n;v++){
            if(graph[u][v]!=inf&&vis[v]==false)
            if(d[v]>d[u]+graph[u][v]){
                d[v]=d[u]+graph[u][v];
                pre[v].clear();
                pre[v].push_back(u);//试过想要输出路径,输出不了原来是这里写错了u为v,直接爆内存
            }else if(d[v]==d[u]+graph[u][v]){
                pre[v].push_back(u);
            }
        }
    }
    
}
void dfs(int ed){
    temppath.push_back(ed);//进来先放置点
    if(ed==0){
        int need=0,remain=0;
        for(int i=temppath.size()-1;i>=0;i--){
            int id=temppath[i];//容易忘了储存当前访问到的结点
            if(weigh[id]>0){
                remain+=weigh[id];
            }
            else {
                if(remain>abs(weigh[id])){
                    remain-=abs(weigh[id]);
                }else{
                    need+=abs(weigh[id])-remain;
                    remain=0;
                }
            }
        }
        if(need<minneed){
            path=temppath;
            minneed=need;
            minremain=remain;
        }else if(need==minneed&&remain<minremain){
            path=temppath;
            minneed=need;
            minremain=remain;
        }
        temppath.pop_back();
        return ;
    }
    for(int i=0;i<pre[ed].size();i++){
        dfs(pre[ed][i]);
        
    }temppath.pop_back();
        
}
int main(){
    cin>>cmax>>n>>ed>>m;
    cmax/=2;
    for(int i=1;i<=n;i++){
        cin>>weigh[i];
        weigh[i]-=cmax;
    }
    fill(graph[0],graph[0]+N*N,inf);
    for(int i=0;i<m;i++){
        int a,b;
        cin>>a>>b;
        cin>>graph[a][b];
        graph[b][a]=graph[a][b];
        
    }
    dijstra(0);
    dfs(ed);
    cout<<minneed<<" ";
    
    for(int i=path.size()-1;i>=0;i--){
        cout<<path[i];
        if(i>0) cout<<"->";
    }
    cout<<" "<<minremain;
    
    return 0;
}

gas station



road to roma

#include <bits/stdc++.h>
using namespace std;
const int N=250;
const int inf =1e9;

int n,m,st,ed,graph[N][N],weigh[N];
int d[N],w[N],num[N],pt[N];
bool vis[N];
int pre[N];
//vector<int>pre[N];
vector<int>temppath,path;
map<string ,int >stringtoint ;
map<int, string >inttostring ;



void dijkstra(int s){
    fill(d,d+N,inf);
    d[s]=0;
    w[s]=weigh[st];
    num[s]=1;
    for(int i=0;i<n;i++){
        pre[i]=i;//这里没有初始化会除0,浮点错误
    }
    for(int i=0;i<n;i++){
        int u=-1,min=inf;
        for(int j=0;j<n;j++){
            if(vis[j]==false &&d[j]<min ){
                min=d[j],u=j;
            }
        }
        if(u==-1) return ;
        vis[u]=true;
        for(int v=0;v<n;v++){
            
            if(graph[u][v]!=inf&&vis[v]==false){
                
                if(d[v]>d[u]+graph[u][v]){
                    d[v]=graph[u][v]+d[u];
                    pt[v]=pt[u]+1;
                    pre[v]=u;
                    num[v]=num[u];
                    w[v]=w[u]+weigh[v];
                }
                else if(d[v]==d[u]+graph[u][v]){
                    
                    num[v]+=num[u];
                    if(w[v]<weigh[v]+w[u]){
                        w[v]=w[u]+weigh[v];
                        pre[v]=u;
                        pt[v]=pt[u]+1;
                        
                    }else if(w[v]==weigh[v]+w[u]){
                         double uavg=1.0*(w[u]+weigh[v])/(pt[u]+1);
                        double vavg=1.0*(w[v])/(pt[v]);
                        if(uavg>vavg){
                            pre[v]=u;
                            pt[v]=pt[u]+1;
                        }
                        
                    }
                
                
                
                }
                    
                
            }
        
            
            
        }
        
        
        
        
    }
}

void dfs(int s){
    if(s==0){
      cout<<inttostring[s];
        return ;
    }
    dfs(pre[s]);
    printf("->");
    cout<<inttostring[s];
    
}
int main(){
    cin>>n>>m;
    
    string a;
    cin>>a;
    stringtoint[a]=0;
    inttostring[0]=a;
    

    fill(graph[0] ,graph[0]+N*N,inf);
    for(int i=1;i<n;i++){
        cin>>a>>weigh[i];
        stringtoint[a]=i;
        inttostring[i]=a;
    }
    for(int i=0;i<m;i++){
        string st1,st2;int s1,s2;cin>>st1>>st2;//这里把st2写成st1 直接寄了
        
        s1=stringtoint[st1],s2=stringtoint[st2];
        cin>>graph[s1][s2];
        graph[s2][s1]=graph[s1][s2];
    }
    dijkstra(st);
    int ed=stringtoint["ROM"];
    cout<<num[ed]<<" "<<d[ed]<<" "<<w[ed]<<" "<<w[ed]/pt[ed]<<endl;
    dfs(ed);
    
    
    
    return 0;
    
}
dfs+dijkstra 
#include <bits/stdc++.h>
using namespace std;
const int N=250;
const int inf =1e9;

int n,m,st,ed,graph[N][N],weigh[N];
int d[N],w[N],num[N],pt[N];
bool vis[N];
vector<int> pre[N];
//vector<int>pre[N];
vector<int>temppath,path;
map<string ,int >stringtoint ;
map<int, string >inttostring ;

void dijkstra(int s){
    fill(d,d+N,inf);
    d[s]=0;
    w[s]=weigh[st];
    num[s]=1;    
    for(int i=0;i<n;i++){
        int u=-1,min=inf;
        for(int j=0;j<n;j++){
            if(vis[j]==false &&d[j]<min ){
                min=d[j],u=j;
            }
        }
        if(u==-1) return ;
        vis[u]=true;
        for(int v=0;v<n;v++){            
            if(graph[u][v]!=inf&&vis[v]==false){                
                if(d[v]>d[u]+graph[u][v]){
                    d[v]=graph[u][v]+d[u];
                   pre[v].clear();
                    pre[v].push_back(u);
                }
                else if(d[v]==d[u]+graph[u][v]){                    
                    pre[v].push_back(u);                  
                    }             
                }
            }          
        }
       
    }

int numpath=0,maxw=0;double maxavg=0;
void dfs(int s){
    temppath.push_back(s);
    if(s==0){
        numpath+=1;
        int tempw=0;
        double tempavg=0;
        for(int i=temppath.size()-1;i>=0;i--){
            tempw+=weigh[temppath[i]] ;
      
        }
        tempavg=1.0*tempw /(temppath.size()-1);
        
        if(tempw>maxw){
            maxw=tempw;
            path=temppath;
            maxavg=tempavg;
            
        }else if(tempw==maxw){
            if(maxavg<tempavg){
                maxavg=maxavg;
                path=temppath;
            }
        }

        temppath.pop_back();
        return ;
    }

    for(int i=0;i<pre[s].size();i++){
        dfs(pre[s][i]);
    }
    
    temppath.pop_back();
    
}
int main(){
    cin>>n>>m;
    
    string a;
    cin>>a;
    stringtoint[a]=0;
    inttostring[0]=a;

    fill(graph[0] ,graph[0]+N*N,inf);
    for(int i=1;i<n;i++){
        cin>>a>>weigh[i];
        stringtoint[a]=i;
        inttostring[i]=a;
    }
    for(int i=0;i<m;i++){
        string st1,st2;int s1,s2;cin>>st1>>st2;//这里把st2写成st1 直接寄了
        
        s1=stringtoint[st1],s2=stringtoint[st2];
        cin>>graph[s1][s2];
        graph[s2][s1]=graph[s1][s2];
    }
    dijkstra(st);
    int ed=stringtoint["ROM"]; dfs(ed);
    cout<<numpath<<" "<<d[ed]<<" "<<maxw<<" "<<(int)maxavg<<endl;
   
    for(int i=path.size()-1;i>=0;i--){
        cout<<inttostring[path[i]] ;
        if(i>0) cout<<"->" ;
    }
    
    return 0;
    
}



online map

#include <bits/stdc++.h>
using namespace std;
const int N=510;
const int inf =1e9;

int n,m,st,ed,graph[N][N],tm1[N][N];
int d[N],t[N],mintime=inf;
bool visd[N],vist[N];
vector<int> pred[N],pret[N];
//vector<int>pre[N];
vector<int>temppath,patht,pathd;

void dijkstra(int s,int graph[][N],int d[],vector<int>pre[] ,bool vis[]){
    fill(d,d+N,inf);
    d[s]=0;
    
    for(int i=0;i<n;i++){
        int u=-1,min=inf;
        for(int j=0;j<n;j++){
            if(vis[j]==false &&d[j]<min ){
                min=d[j],u=j;
            }
        }
        if(u==-1) return ;
        vis[u]=true;
        for(int v=0;v<n;v++){
            
            if(graph[u][v]!=inf&&vis[v]==false){
                
                if(d[v]>d[u]+graph[u][v]){
                    d[v]=graph[u][v]+d[u];
                   pre[v].clear();
                    pre[v].push_back(u);
                }
                else if(d[v]==d[u]+graph[u][v]){
                    
                    pre[v].push_back(u);
                    
                    }             
                }
            }        
        }
       
    }

int numpath=0,maxw=0;double maxavg=0;


void getd(){
    int temptime=0;
    for(int i=temppath.size()-1;i>0;i--){
        temptime+=tm1[temppath[i]][temppath[i-1]];
        
    }
    if(temptime<mintime){
        mintime=temptime;
        pathd=temppath;
    }
}
void gett(){
    if(patht.size()==0||temppath.size()<patht.size()){
        patht=temppath;//最有路径为空,或者找到了顶点个数更小的路
  
//		cout<<"path长度等于"<<temppath.size();
    }
}
void dfs(int v,vector<int>pre[],bool isfirstpath){
    temppath.push_back(v);
    if(v==st ){
        
    if(isfirstpath) getd();
        else gett();
        temppath.pop_back();//这里忘记了pop 所以输出了两个终点答案
        return ;
    }
    for(int i=0;i<pre[v].size();i++){
        dfs(pre[v][i],pre,isfirstpath);
    }
    temppath.pop_back();
    
    
    
    
}
void printfpath(vector<int>path){
    cout<<path.back();
    for(int i=path.size()-2;i>=0;i--){
        cout<<" -> "<<path[i];
    }cout<<endl;
}
int main(){
    cin>>n>>m;
    int a,b,oneway;
    fill(graph[0], graph[0]+N*N,inf );
    fill(tm1[0],tm1[0]+N*N,inf);
    for(int i=0;i<m;i++){
        cin>>a>>b>>oneway;//0是双向
        cin>>graph[a][b];
        cin>>tm1[a][b];
        if(oneway==0){
            graph[b][a]=graph[a][b];
            tm1[b][a]=tm1[a][b];
        }

    }
    
    cin>>st>>ed;
    dijkstra(st,graph,d,pred,visd);
    dijkstra(st,tm1,t,pret,vist);
    int c=ed;

    dfs(ed,pred,1);
    dfs(ed,pret,0);
   cout<<"Distance = "<<d[ed];
    if(pathd==patht) cout<<"; ";
    else {
        cout<<": ";
        printfpath(pathd);
    }
    printf("Time = %d: ",t[ed]);
//	patht.pop_back(); 
    printfpath(patht);

    return 0;
    
}

bellman算法
由于dijstra一开始需要选择dsit最小的点作为选择,如果此时出现的边不是最优选择的话(因为别的路线可能有一个很大的赋值,掩盖了第一次选择的问题),

posted @ 2021-12-20 13:21  liang302  阅读(29)  评论(0编辑  收藏  举报