int G[N][N];
int path[N][N];//path[i][j]=x表示i到j的路径上除i外的第一个点是x
void init(int n) {
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            if (i == j)
                G[i][j] = 0;
            else
                G[i][j] = INF;
            path[i][j] = j;
        }
    }
}
void floyd(int n) {
    for (int k = 1; k <= n; k++) {         //枚举中间点
        for (int i = 1; i <= n; i++) {     //枚举起点
            for (int j = 1; j <= n; j++) { //枚举终点
                if (G[i][k] < INF && G[k][j] < INF) {
                    if (G[i][j] > G[i][k] + G[k][j]) { //松弛操作
                        G[i][j] = G[i][k] + G[k][j];
                        path[i][j] = path[i][k]; //更新路径
                    } 
                    else if (
                        G[i][j] == G[i][k] + G[k][j] && path[i][j] > path[i][k]) { //在最短路相同的情况下,更新字典序最小的路径
                        path[i][j] = path[i][k]; //最终path中存的是字典序最小的路径
                    }
                }
            }
        }
    }
}
int main() {
    int n, m;
    while (scanf("%d%d", &n, &m) != EOF) {
        init(n);
        for (int i = 1; i <= m; i++) {
            int x, y, dis;
            scanf("%d%d%d", &x, &y, &dis);
            //无向图添边一次,有向图添边两次
            G[x][y] = dis;
            G[y][x] = dis;
        }
 
        floyd(n);
 
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= n; j++)
                printf("%d ", G[i][j]);
            printf("\n");
        }
    }
    return 0;
}

Floyd 算法又称为插点法,是一种用于寻找给定的加权图中多源点之间最短路径的算法。

其最大特点是可以计算出现负边权时的最短路,实际应用中,很多题目不是问如何用 Floyd 求最短路,而是用 Floyd 的动态规划思想来解决类似 Floyd 的问题。

其时间复杂度是 O(NNN),N是顶点数。

int dis[N];//单源最短距离
int G[N][N];//G[i][j]表示i到j的有向边长
bool vis[N];//表示w[i]是否已经计算完
void dijkstra(int n,int s){
    for(int i=1;i<=n;i++){
        int x;//x标记当前最短w的点
        int min_dis=INF;//记录当前最小距离
 
        for(int y=1;y<=n;y++){
            if(!vis[y] && min_dis>=dis[y]){
                x=y;
                min_dis=dis[x];
            }
        }
 
        vis[x]=true;
 
        for(int y=1;y<=n;y++) 
            dis[y]=min(dis[y],dis[x]+G[x][y]);
    }
}
int main(){
    memset(dis,INF,sizeof(dis));
    memset(vis,0,sizeof(vis));
 
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
        int x,y,dis;
        scanf("%d%d%d",&x,&y,&dis);
        G[x][y] = G[y][x] = dis; //无向图添边一次,有向图添边两次
    }
    int start;
    scanf("%d",&start);
    dijkstra(n,start);
    for(int i=1;i<=n;i++)
        printf("%d\n",dis[i]);
    return 0;
}
int n,m;
struct Edge{//边
    int from;//边的起点
    int to;//边的终点
    int dis;//边的长度
    Edge(int f,int t,int d){//构造函数
        from=f;
        to=t;
        dis=d;
    }
};
 
struct HeapNode{//Dijkstra用到的优先队列的结点
    int dis;//点到起点距离
    int u;//点的序号
    HeapNode(int a,int b){
        dis=a;
        u=b;
    }
    bool operator < (const HeapNode &rhs) const  {
        return dis > rhs.dis;
    }
};
 
struct Dijkstra{
    int n,m;//点数、边数
    vector<Edge> edges;//边列表
    vector<int> G[N];//每个结点出发的边的编号
    bool vis[N];//是否走过
    int dis[N];//起点s到各点的距离
    int p[N];//从起点s到i的最短路中的最后一条边的编号
 
    void init(int n) {//初始化
        this->n = n;
        for(int i=0;i<n;i++) //清空邻接表
            G[i].clear();
        edges.clear();//清空边列表
    }
 
    void AddEdge(int from,int to,int diss) {//添加边,若为无向图,调用两次
        edges.push_back(Edge(from,to,diss));
        m=edges.size();//边的个数
        G[from].push_back(m-1);//添加至边列表
    }
 
    int dijkstra(int s) {//求s到所有点的距离
 
        memset(dis,INF,sizeof(dis));
        memset(vis,false,sizeof(vis));
        dis[s]=0;
 
        priority_queue<HeapNode> Q;//优先队列
        Q.push(HeapNode(0,s));
        while(!Q.empty()) {
            HeapNode x=Q.top();
            Q.pop();
 
            int u=x.u;
            if(vis[u])//若已被访问
                continue;
 
            vis[u]=true;//标记为已访问
            for(int i=0;i<G[u].size();i++) {//枚举所有与当前点相连的边
                Edge &e=edges[G[u][i]];
                if(dis[e.to] > dis[u]+e.dis) {//更新距离
                    dis[e.to] = dis[u]+e.dis;
                    p[e.to]=G[u][i];
                    Q.push(HeapNode(dis[e.to],e.to));
                }
            }
        }
        return dis[n];//返回起点s到终点n最短距离
    }
}DJ;
 
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF&&(n+m))
    {
        DJ.init(n);//初始化
        for(int i=0;i<m;i++) {
            int x,y,dis;
            scanf("%d%d%d",&x,&y,&dis);
            //无向图添边两次
            DJ.AddEdge(x,y,dis);
            DJ.AddEdge(y,x,dis);
        }
 
        int start;
        scanf("%d",&start);
        DJ.dijkstra(start);//求start到各点的距离
        for(int i=0,j=0,s=++start;i<n;i++)//输出start到各点的距离
            printf("%d->%d: %d\n",s,++j,DJ.dis[i]);
    }
    return 0;
}

Dijkstra 算法的思想,就是一开始将起点到终点的距离标记为 0,而后进行 n 次循环,每次找出一个到起点距离 dis[u] 最短的点 u ,将它从蓝点变为白点,随后枚举所有白点 Vi,如果以此白点为中转到达蓝点 Vi 的路径 dis[u]+w[u][vi] 更短的话,这将它作为 Vi 的更短路径(此时还不能确定是不是Vi的最短路径)。
Dijkstra 算法是单源最短路径算法,即计算起点只有一个的情况到其他点的最短路径,其无法处理存在负边权的情况。

其时间复杂度是:O(E+VlogV)

SPFA
Bellman-Ford算法适用于计算单源最短路径,即:只能计算起点只有一个的情况。

其最大特点是可以处理存在负边权的情况,但无法处理存在负权回路的情况。

其时间复杂度为:O(V*E),其中,V 是顶点数,E 是边数。

【算法分析】
Bellman Ford 算法与 Dijkstra 算法的思想相同,只不过 Dijkstra 是每次确定一个最短距离点,并用这个点去更新与之相连的其他边,而 Ford 算法是每次更新所有的边,从而确定一个点的最短距离
————————————————

int dis[N];
bool vis[N];
void SPFA(int S) {
    memset(vis, false, sizeof(vis));
    memset(dis, INF, sizeof(dis));
    dis[S] = 0;
 
    queue<int> Q;
    Q.push(S);
 
    while (!Q.empty()) {
        int x = Q.front();
        Q.pop();
        vis[x] = false;
        for (int i = head[x]; i != -1; i = edge[i].next) {
            int to = edge[i].to;
            if (dis[to] > dis[x] + edge[i].dis) {
                dis[to] = dis[x] + edge[i].dis;
                if (!vis[to]) {
                    vis[to] = true;
                    Q.push(to);
                }
            }
        }
    }
}

优化spfa

struct Edge{
    int to,dis;
};
vector<Edge> edge[N];
bool vis[N];
int dis[N];
void SPFA(int s) {
    memset(dis, INF, sizeof(dis));
    memset(vis, false, sizeof(vis));
    vis[s] = true;
    dis[s] = 0;
    
    deque<int> Q;
    Q.push_back(s);
    while (!Q.empty()) {
        int x = Q.front();
        Q.pop_front();
        vis[x] = 0;
        for (int i = 0; i < edge[x].size(); i++) {
            int y = edge[x][i].to;
            if (dis[y] > dis[x] + edge[x][i].to) {
                dis[y] = dis[x] + edge[x][i].to;
                if (!vis[y]) {
                    vis[y] = true;
                    if (!Q.empty() && dis[y] < dis[Q.front()])//加入队首
                        Q.push_front(y);
                    else//加入队尾
                        Q.push_back(y);
                }
            }
        }
    }
}

标准

struct Edge {
    int to, next;
    int dis;
} edge[N];
int head[N], tot;
bool vis[N];
int dis[N];
void addEdge(int x, int y, int dis) {
    edge[++tot].to = y;
    edge[tot].dis = dis;
    edge[tot].next = head[x];
    head[x] = tot;
}
void SPFA(int S) {
    memset(vis, false, sizeof(vis));
    memset(dis, INF, sizeof(dis));
    dis[S] = 0;
 
    queue<int> Q;
    Q.push(S);
 
    while (!Q.empty()) {
        int x = Q.front();
        Q.pop();
        vis[x] = false;
        for (int i = head[x]; i != -1; i = edge[i].next) {
            int to = edge[i].to;
            if (dis[to] > dis[x] + edge[i].dis) {
                dis[to] = dis[x] + edge[i].dis;
                if (!vis[to]) {
                    vis[to] = true;
                    Q.push(to);
                }
            }
        }
    }
}
int main(){
    int n,m;
    while(scanf("%d%d",&n,&m)!=EOF){
        memset(head,-1,sizeof(head));
        for(int i=1;i<=m;i++){
            int x,y,dis;
            scanf("%d%d%d",&x,&y,&dis);
            //无向图添边两次
            addEdge(x,y,dis);
            addEdge(y,x,dis);
        }
        int S;
        scanf("%d",&S);
        SPFA(S);
        for(int i=1;i<=n;i++)
            printf("%d\n",dis[i]);
    }    
    return 0;
}

用spfa判断负环

struct Edge {
    int from, to;
    int dis;
    Edge() {}
    Edge(int from, int to, int dis) : from(from), to(to), dis(dis) {}
};
struct SPFA {
    int n, m;
    Edge edges[N]; //所有的边信息
    int head[N];   //每个节点邻接表的头
    int next[N];   //每个点的下一条边
    int pre[N];    //最短路中的上一条弧
    bool vis[N];
    int dis[N];
    int cnt[N]; //进队次数
 
    void init(int n) {
        this->n = n;
        this->m = 0;
        memset(head, -1, sizeof(head));
    }
 
    void AddEdge(int from, int to, int dist) {
        edges[m] = Edge(from, to, dist);
        next[m] = head[from];
        head[from] = m++;
    }
 
    bool negativeCycle(int s) { //判负环
        memset(vis, false, sizeof(vis));
        memset(cnt, 0, sizeof(cnt));
        memset(dis, INF, sizeof(dis));
        dis[s] = 0;
 
        queue<int> Q;
        Q.push(s);
 
        while (!Q.empty()) {
            int x = Q.front();
            Q.pop();
            vis[x] = false;
            for (int i = head[x]; i != -1; i = next[i]) {
                Edge &e = edges[i];
                if (dis[e.to] > dis[x] + e.dis) {
                    dis[e.to] = dis[x] + e.dis;
                    pre[e.to] = i;
                    if (!vis[e.to]) {
                        vis[e.to] = true;
                        Q.push(e.to);
                        if (++cnt[e.to] > n)
                            return true;
                    }
                }
            }
        }
        return false;
    }
} spfa;
int main() {
    int n, m;
    while (scanf("%d%d", &n, &m) != EOF) {
        spfa.init(n);
        int S;
        scanf("%d", &S);
        for (int i = 1; i <= m; i++) {
            int x, y, dis;
            scanf("%d%d%d", &x, &y, &dis);
            //无向边添边两次
            spfa.AddEdge(x, y, dis);
            spfa.AddEdge(y, x, dis);
        }
        spfa.negativeCycle(S);
        for (int i = 1; i <= n; i++)
            printf("%d\n", spfa.dis[i]);
    }
    return 0;
}

差分约束

求解差分约束系统的关键在于建图,建好图后使用 SPFA 算法直接判断有无负环即可判断该差分约束系统有无解。

具体方法:

1.新建一个图,n 个变量看作 n 个点,m 个约束条件作为 m 条边,每个顶点 Vi 分别对于一个未知量,每个有向边对应两个未知量的不等式。

1)对于 <= 的不等式,形如:dis[j]-dis[i]<=w,可化为 dis[j]<=dis[i]+w,建立从 i 到 j 权值为 w 的边

2)对于 >= 的不等式,形如:dis[j]-dis[i]>=w,可化为 dis[i]<=dis[j]-w,建立从 j 到 i 权值为 -w 的边

2.根据约束条件,进行初始化

1)若求差最大,则:dis[1]=0 且 dis[i]=INF

2)若求差最小,则:dis[1]=0 且 dis[i]=-INF

3.根据实际情况判断是否需要添加超级源点 Vs(0 号点)

1)若建成的图能保证连通,则直接求最短路即可,若建成的图不是连通图,为保证图的连通性,需要加一个源点 Vs,从 Vs 到任意点 Vi 的边权为 0,即:W(Vs,Vi)=0,然后从该点出发进行计算,那么最终求出源点 Vs 到其他所有点的最短距离就是差分约束系统的一个可行解,且可行解之间的差距最小。

   原因:添加从虚点 Vs 到各个顶点 Vi 的权为 0 的边,是为了保证构造出的图是连通的,且由于虚点本身并不引入负环,因此设置虚点 Vs 后最短路仍然存在且每个约束仍满足。

2)若不添加超级源点,只是将初始距离设为 INF,且令其中一个点的初始距离为 0,然后就该点到其他所有点的最短距离,那么最短距离的集合就是一个可行解,且该可行解两两之间差距最大。

适用情况:保证问题一定存在解,即:不存在负环(否则从 1 号点到其他点没有路,但其他点的强连通分量中有负环)

4.根据结果进行解的判断

1)若源点 Vs 到某点 Xi 不存在最短路径,即:dis[Xi]=INF,则表示该点 Xi 表示的变量可取任意解,均能满足差分约束的要求

2)若存在源点 Vs 到某点 Xi 的最短路径,则得到该点 Xi 表示的变量的最大值

3)若求最短路的过程中出现负环,则该差分约束系统无解。

注:若所有的未知量都是正数,则不会存在负环,使用 Dijkstra 求解单源最短路即可;若存在负数,需使用 SPFA 来求解,判断有无负环

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define INF 0x3f3f3f3f
#define N 1000001
struct Edge{
    int from;
    int to;
    int w;
    Edge(){}
    Edge(int from,int to,int w):from(from),to(to),w(w){}
}edge[N];
int head[N],next[N],num;
int dis[N];
bool vis[N];
void add(int from,int to,int w){
    edge[num]=Edge(from,to,w);
    next[num]=head[from];
    head[from]=num++;
}
struct HeapNode{
    int dis;
    int x;
    HeapNode(int dis,int x):dis(dis),x(x){}
    bool operator < (const HeapNode &rhs) const{
        return dis>rhs.dis;
    }
};
int dijkstra(int n){
    for(int i=0;i<n;i++)
        dis[i]=INF;
    dis[0]=0;
 
    priority_queue<HeapNode> Q;
    Q.push(HeapNode(dis[0],0));
    while(!Q.empty()){
        HeapNode x=Q.top();
        Q.pop();
 
        int u=x.x;
        if(vis[u])
            continue;
        vis[u]=true;
        for(int i=head[u];i!=-1;i=next[i]){
            Edge &e=edge[i];
            if(dis[e.to]>dis[u]+e.w){
                dis[e.to]=dis[u]+e.w;
                Q.push(HeapNode(dis[e.to],e.to));
            }
        }
    }
    return dis[n-1];
}
int main(){
    int n,m;
    scanf("%d%d",&n,&m);
 
    num=0;
    memset(head,-1,sizeof(head));
 
    while(m--){
        int x,y,w;
        scanf("%d%d%d",&x,&y,&w);
        x--,y--;
        add(x,y,w);
    }
 
    int res=dijkstra(n);
    if(res==INF)
        printf("arbitrary");
    else//源点到终点的最大值
        printf("%d\n",res);
 
    return 0;
}
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define INF 0x3f3f3f3f
#define N 10001
struct Edge{
    int from;
    int to;
    int w;
    Edge(){}
    Edge(int from,int to,int w):from(from),to(to),w(w){}
}edge[N];
int head[N],next[N],num;
int dis[N];
int outque[N];//记录出队次数
bool vis[N];
void init(){
    num=0;
    memset(head,-1,sizeof(head));
}
void add(int from,int to,int w){
    edge[num]=Edge(from,to,w);
    next[num]=head[from];
    head[from]=num++;
}
void SPFA(int s,int n){
    int res=0;
    memset(vis,false,sizeof(vis));
    memset(outque,0,sizeof(outque));
 
    for(int i=1;i<n;i++)
        dis[i]=INF;
    dis[s]=0;
    vis[s]=true;
 
    queue<int> Q;
    Q.push(s);
    while(!Q.empty()){
        int x=Q.front();
        Q.pop();
        vis[x]=false;
 
        outque[x]++;
        if(outque[x]>n-1){//如果出队次数大于n,说明存在负环
            res=-1;
            break;
        }
 
        for(int i=head[x];i!=-1;i=next[i]){
            Edge &e=edge[i];
            if(dis[e.to]>dis[x]+e.w){
                dis[e.to]=dis[x]+e.w;
                if(!vis[e.to]){
                    vis[e.to]=true;
                    Q.push(e.to);
                }
            }
        }
    }
 
    if(res==-1)//出现负环,不存在可行解
        printf("-1\n");
    else if(dis[n]==INF)//可取任意值
        printf("arbitrary\n");
    else//源点s到终点的最大值
        printf("%d\n",dis[n]);
}
int main(){
    int n,m;
    while(scanf("%d%d",&n,&m)!=EOF){
        init();
        for(int i=0;i<m;i++){
            int x,y,w;
            scanf("%d%d%d",&x,&y,&w);
            add(x,y,w);//建边x-y<=w
        }
        //虚拟节点0号到各点的边
        //for(int i=1;i<=n;i++)
            //add(0,i,0);
        SPFA(1,n);//求源点s到终点的最大值,若添加虚拟节点0号后,需改为SPFA(0,n+1)
    }
    return 0;
}

P5318
你或许需要 一个结构体存储父亲和儿子
需要一个动态数组来存放这些结构体
需要一个动态数组存具体二维信息

输入关系,然后打包成结构体存进一个数组里,创造一个cmp,排序大头和小头
好,排序好的是从第一本到最后一本,下面是第一本下面第一个要看的到最后一个要看到的,每一个都有它对应的结构体里的爸爸,所以再次遍历,一个数组里存结构体里的爸爸,爸爸下面插入,插入本身在数组里就是有顺序的,然后插入的就是i 就是向向自己的爸爸中插入自己 由此形成了可以深搜广搜的结构

用一个结构体vector(为了节省空间,咱用vector来存)存储每个边的起点和终点,然后用一个二维vector(也就是一个vector数组)存储边的信息。

这个存储方法可能有点难理解,不过其实也没那么难:我们用ee[aa][bb]=cc,来表示顶点aa的第bb条边是cc号边。咱举个栗子,还是拿样例说吧:

8 9
1 2 //0号边(由于vector的下标是从0开始的,咱就“入乡随俗”,从0开始)
1 3 //1号边
1 4 //2号边
2 5 //3号边
2 6 //4号边
3 7 //5号边
4 7 //6号边
4 8 //7号边
7 8 //8号边

0 1 2 //1号顶点连着0、1、2号边
3 4	//2号顶点连着3、4号边
5	//3号顶点连着5号边
6 7 //4号顶点连着6、7号边
	//5号顶点没有边
	//6号顶点没有边
8	//7号顶点连着8号边
	//8号顶点没有边

广搜用队列,把1入队,
队伍中,找出对头,循环里面是对对头的儿子们遍历,每一次找出他们的儿子,然后看儿子是否入队,没有就入对并且打印;最后记得出对一个

void bfs(int x){  //广度优先遍历
	queue <int> q;
	q.push(x);
	cout<<x<<" ";
	vis2[x]=1;
	while(!q.empty()){
		int fro=q.front();
		for(int i=0;i<e[fro].size();i++){
			int point=s[e[fro][i]].v;
			if(!vis2[point]){
				q.push(point); 
				cout<<point<<" ";
				vis2[point]=1;
			}
		}
		q.pop();
	}
}

深度优先搜索
用递归,先进来,然后找儿子,找儿子,步步深入

void dfs(int x){  //深度优先遍历
	vis1[x]=1;
	cout<<x<<" ";
	for(int i=0;i<e[x].size();i++){
		int point=s[e[x][i]].v;
		if(!vis1[point]){
			dfs(point);
		}
	}
}
/*#include<iostream>
#include<vector>
#include<queue>
#include<algorithm>
using namespace std;
int vis1[100001],vis2[1000001];
struct edge
{
	int u;
	int v;
};/存储每一对关系 
int m,n,x,y;
bool cmp(edge x,edge y)
{
	if(x.v==y.v ) return x.u <y.u ;
	else return x.v <y.u ;}
vector<edge>s;//存储关系的数组 
vector<int>e[1000000];//存储每一文章里的文献是第几个 
void dfs(int x)///深搜:由1个点,找到他的那些小的 
{
	vis1[x]=1;
	cout<<x<<" ";
	for(int i=0;i<e[x].size();i++)
	{
		int point=s[e[x][i]].v;
		if(!vis1[point])
		{
			dfs(point);
		}
	}
}
void bfs(int x)
{
	queue<int> q;
	q.push(x);
	cout<<x<<" ";
	vis2[x]=1;
	while(!q.empty())
	{
		int fro=q.front();
		for(int i=0;i<e[fro].size();i++)
		{
			int point=s[e[fro][i]].v;
			if(!vis2[point])
			{
				q.push(point);
				cout<<point<<" ";
				vis2[point]=1;
			}
		}
		q.pop();
	}
}
int main()
{
	
	cin>>m>>n;
	for(int i=0;i<n;i++)
	{
		cin>>x>>y;
		s.push_back((edge){x,y});		
	}
	sort(s.begin(),s.end(),cmp);
	for(int i=0;i<n;i++)///一定要从0开始,而不是1 
	{
		e[s[i].u].push_back(i);
	}
	dfs(1);
	cout<<endl;
	bfs(1);
	return 0;
}*/
#include<iostream>  
#include<vector>
#include<queue>
#include<algorithm>
using namespace std;  
struct edge{    
	int u,v;
}; 
vector <int> e[100001]; 
vector <edge> s;
bool vis1[100001]={0},vis2[100001]={0}; 
bool cmp(edge x,edge y){
	if(x.v==y.v)
	return x.u<y.u;
	else return x.v<y.v;
}
void dfs(int x){
	vis1[x]=1;
	cout<<x<<" ";
	for(int i=0;i<e[x].size();i++){
		
		if(!vis1[e[x][i]){
			dfs(e[x][i]);
		}
	}
}
void bfs(int x){
	queue <int> q;
	q.push(x);
	cout<<x<<" ";
	vis2[x]=1;
	while(!q.empty()){
		int fro=q.front();
		for(int i=0;i<e[fro].size();i++){
			int point=s[e[fro][i]].v;
			if(!vis2[point]){
				q.push(point); 
				cout<<point<<" ";
				vis2[point]=1;
			}
		}
		q.pop();
	}
}
int main(){
	int n,m;
	cin>>n>>m; 
	for(int i=0;i<m;i++){
		int uu,vv;
		cin>>uu>>vv;
		s.push_back((edge){uu,vv});   
	}
	sort(s.begin(),s.end(),cmp);
	for(int i=0;i<m;i++)   
		e[s[i].u].push_back(s[i].v); 
	dfs(1);   
	cout<<endl;
	bfs(1);   
}
#include<bits/stdc++.h>
using namespace std;
int n,m;
set<int>e[100009];
bool vis[100009];
void init(){
	for(scanf("%d%d",&n,&m);m;--m){
		int u=0,v=0;
		scanf("%d%d",&u,&v);
		e[u].insert(v);
	}
}
void dfs(int x=1){
	if(vis[x])return;vis[x]=1;
	printf("%d ",x);
	for(int v:e[x])dfs(v);
}
void bfs(){
	queue<int>q;
	q.push(1);
	while(!q.empty()){
		int x=q.front();q.pop();
		if(vis[x])continue;vis[x]=1;
		printf("%d ",x);
		for(int v:e[x])q.push(v);
	}
}
int main(){
	init();
	dfs();
	putchar('\n');
	memset(vis,0,sizeof(vis));
	bfs();
	putchar('\n');
	return 0;
}

P3916
出发到达的最远的点:反向考虑最远的点在哪,循环从n到1,则每个点i能访问到的结点的a值都是i,每个点访问一次,这个a最优

反向存边+从大到小便利+有过就返回==确保每个都是最远的,dfs里不变的参量也是有价值的,可以用来赋值

#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;

#define MAXL 100010

int N, M, A[MAXL];
vector<int> G[MAXL]; //vector存图 

void dfs(int x, int d) {
	if(A[x]) return; //访问过 
	A[x] = d;这个也太diao了吧
	for(int i=0; i<G[x].size(); i++)
		dfs(G[x][i], d);
}

int main() {
	int u, v;
	scanf("%d%d", &N, &M);
	for(int i=1; i<=M; i++) {
		scanf("%d%d", &u, &v);
		G[v].push_back(u); //反向建边 
	}
	for(int i=N; i; i--) dfs(i, i); 
	for(int i=1; i<=N; i++) printf("%d ", A[i]);
	printf("\n");
	return 0;
}


P1113
其实就是寻找一条最长的链,应该先把自己的时间和工作匹配对应上,然后取自己的子任务中较长的为准,以此类推向后

拓扑排序模板题目,每一次记得更新

#include<iostream>
#include<vector>
#include<queue>
using namespace std;
vector<int>ru[9000];
queue<int>qu;
int m,h,g,rut[9000],dp[9000],tim[9000] ,p,ans;
int main()
{
	cin>>m;
	for(int i=1;i<=m;i++)
	{
		cin>>h>>g;
		tim[h]=g;
		while(cin>>p&&p)///很多个就是while
		{
			ru[h].push_back(p);//存到我的代做列表里
			rut[p]++;//为了后面寻找开头的事项
		}
	}
    for(int i=1;i<=m;i++)
    {
    	if(!rut[i])//有点拓扑的味道
    	{
    	qu.push(i);
		dp[i]=tim[i]; 
		}
	}
	while(!qu.empty())
	{
		int now=qu.front();//就是干完的,他的下面的任务可以开始了
		qu.pop();
		for(int i=0;i<ru[now].size();i++)//下面的任务
		{
			int next=ru[now][i];//它的子任务
			dp[next]=max(dp[next],dp[now]+tim[next]);//而完成工作 X 所需的时间和所有 X 所依赖的工作完成的时间的最大值有关。(就是说应该每一次都更新,因为你很可能是从多个任务过来的,所以每一次新的入队对于这些还没有入对的要有新的更新)
			rut[next]--;//每个子任务的入度-1,就是拓扑
			if(rut[next]==0)
			qu.push(next);//这是一个累加的过程 
		}
	}
	for(int i=1;i<=m;i++)
	{
		ans=max(ans,dp[i]);就是最后一个完成任务的时间啊
	}
	cout<<ans;
	return 0;
} 

P1807
带权有向无环图
或许floyed可以

#include<iostream>
using namespace std;
const int inf=0x7fffffff;
int n,m,x,y,z,map[1007][1007];
int main()
{
	cin>>n>>m;
	if(m==0) {
		cout<<-1;
		return 0;
	}
	for(int i=0;i<n;i++)
	{
		for(int j=0;j<n;j++)
		{
			if(i==j) map[i][j]=0;
			else map[i][j]=inf;
		}///初始化
	}
	for(int i=0;i<m;i++)
	{
		cin>>x>>y>>z;
		x--;y--;
		map[x][y]=min(map[x][y],-z); 更新
	}
	for(int k=0;k<n;k++)//dp
	{
		for(int i=0;i<n;i++)
		{
			for(int j=0;j<n;j++)
			{
				if(map[i][k]!=inf&&map[k][j]!=inf)
				map[i][j]=min(map[i][j],map[i][k]+map[k][j]); 
			}
		}
	}
	if(map[0][n-1]==inf) cout<<-1;
	else cout<<-map[0][n-1];
 } 

当然,广搜也是可以的

#include<cstdio>
#include<cstring>
#include<queue>
#include<iostream>
using namespace std;
const int maxn=1502;
queue<int>q;//广搜队列 
int d[maxn],mp[maxn][maxn];//d[i]记录i结点前的最长路,mp[a][b]存储结点a,b间的路径长 
int n,m;
void bfs()
{
    memset(d,-1,sizeof(d)); //初始化为-1,便于无解时输出的d[n]为-1 
    d[1]=0;//但第一个结点前路径长为0 
    q.push(1);
    while(!q.empty())
    {
        int t=q.front();
        q.pop();
        for(int i=1;i<=n;i++)
            if(mp[t][i]&&d[i]<d[t]+mp[t][i])//如果从队列头到当前结点i有边,但不是最长(即此点的最长路小于队列头最长路加t-i的路径长) 
            {
                d[i]=d[t]+mp[t][i];//更新最长路 
                q.push(i);//进入队列,搜索下一节点 
            }
    }
}
int main()
{
    cin>>n>>m;
    int a,b,v;
    for(int i=1;i<=m;i++)
    {
        cin>>a>>b>>v;
        mp[a][b]=max(mp[a][b],v);//如果两点之间有多条连边,只需保留最长边 
    }
    bfs();
    cout<<d[n];//输出最后一点的最长路即可
    return 0;    
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值