20240203-图论随记

最短路

负环判断

#include <bits/stdc++.h>
using namespace std;
struct node{
    int from,to,v;
}edge[100005];
#define oo 2000000000
int dis[100005];
int main(){
    int n,m,s,t;
    cin>>n>>m>>s>>t;
    for(int i=1;i<=m;i++){
        scanf("%d %d %d",&edge[i].from,&edge[i].to,&edge[i].v);
    }
    for(int i=1;i<=n;i++) dis[i]=oo;
    dis[s]=0;
    bool ok=true;
    for(int i=1;i<=n;i++){
        bool f=1;
        for(int j=1;j<=m;j++){
            int a=edge[j].from;
            int b=edge[j].to;
            int c=edge[j].v;
            if(dis[a]==oo) continue;
            if(dis[b]>dis[a]+c){
                dis[b]=dis[a]+c;
                f=0;
            }
        }
        if(f) break;
        if(i==n) ok=false;
    }
}
SPFA和Bellman-ford的相似之处和区别?
void SPFA(int s){
    for(int i=1;i<=n;i++) dis[i]=oo;
    dis[s]=0;
    int l=0,r=0;
    q[r++]=s;
    while(l<r){
        int k=q[l++];
        for(int i=0;i<edge[k].size();i++){
            int to=edge[k][i].to;
            int v=edge[k][i].v;
            if(dis[to]>dis[k]+v) {
                dis[to]=dis[k]+v;
                if(!mark[to]){
                    mark[to]=1;
                    q[r++]=to;
                    if(++cnt[to]==n)
                }
            }
        }
    }
}
#include <bits/stdc++.h>
using namespace std;
int n, m,h[10005],w[10005],e[10005],nxt[10005],id,dis[10005],cnt[10005];
bool mark[10005];
void add(int a,int b,int c){
    e[id]=b,w[id]=c,nxt[id]=h[a],h[a]=id++;
}
bool spfa() {
    stack<int> stk;
    for (int i=1;i<=n;i++){
        stk.push(i);
        mark[i]=true;
    }
    while (stk.size()){
        int t=stk.top();
        stk.pop();
        mark[t]=false;
        for (int i=h[t];~i;i=nxt[i]){
            int j=e[i];
            if (dis[j]>dis[t]+w[i]){
                dis[j]=dis[t]+w[i];
                cnt[j]=cnt[t]+1;
                if (cnt[j]>=n) return true;
                if (!mark[j]){
                    stk.push(j);
                    mark[j]=true;
                }
            }
        }
    }
    return false;
}
int main(){
    memset(h,-1,sizeof(h));
    cin>>n>>m;
    while (m--){
        int a,b,c;
        cin>>a>>b>>c;
        add(a,b,c);
    }
    if(spfa()) cout<<"YES";
    else cout<<"NO";
    return 0;
}

Dijkstra 堆优化

struct node{
    int to,v;
    bool operator <(const node &a) const{
        return v>a.v;
    }
}
priority_queue<node> q;
bool mark[]
int main(){
    for(int i=1;i<=n;i++){
        dis[i]=oo;
        mark[i]=0;
    }
    dis[s]=0;q.push((node){s,0});
    while(!q.empty()){
        node tmp=q.top();q.pop();
        int k=tmp.to;
        if(mark[k]) continue;
        mark[k]=1;
        for(int j=0;j<edge[k].size();j++){
            int to=edge[k][j].to;
            int v=edge[k][j].v;
            if(dis[to]>dis[k]+v){
                dis[to]=dis[k]+v;
                q.push((node){to,dis[to]});
            }
        }
    }
}

关键点

S ----> X ----> T
   c1      c2
   c1*c2=S---->T

关键边

总统要回家,即从走到,会经过一些街道,每条街道都是单向的并且拥有权值。现在,为了让总统更好的回家,要对每一条街道进行操作:
①如果该街道一定在最短路上,则输出“YES”。
②如果该街道修理过后,该边所在的最短路可以取代原先的最短路,则输出“CAN x”,x是修改该街道的最小花费,就是权值减小的值。
③如果该街道不在到的路径上,或者修改过后权值小于等于0,则输出“NO”。
#include <bits/stdc++.h>
#define ll long long
#define oo 2000000000
using namespace std;
long long a[100005],b[100005],l[100005],d1[100005],d2[100005],dfn[100005],low[100005],n,m,s,t,id;
bool bridge[100005],vis[100005];
vector<pair<long long,long long>> edge[100005];
void dijkstra(long long v,long long *d){
    long long t;
    priority_queue<pair<long long,long long>,vector<pair<long long,long long>>,greater<pair<long long,long long>>> q;
    for(long long i=1;i<=n;i++) d[i]=oo;
    d[v]=0;q.push(make_pair(0,v));
    while(!q.empty()){
        t=q.top().second;q.pop();
        for(long long i=0;i<edge[t].size();i++){
            if(d[edge[t][i].first]>d[t]+edge[t][i].second){
                d[edge[t][i].first]=d[t]+edge[t][i].second;
                q.push(make_pair(d[edge[t][i].first],edge[t][i].first));
            }
        }
    }
    return;
}
void tarjan(long long x){
    id++;
    dfn[x]=id;
    low[x]=id;
    for(long long i=0;i<edge[x].size();i++){
        if(vis[edge[x][i].second]) continue;
        vis[edge[x][i].second]=true;
        if(!dfn[edge[x][i].first]){
            tarjan(edge[x][i].first);
            low[x]=min(low[x],low[edge[x][i].first]);
            if(dfn[x]<low[edge[x][i].first]) bridge[edge[x][i].second]=true;
        }
        else low[x]=min(low[x],dfn[edge[x][i].first]);
    }
    return;
}
int main(){
    cin>>n>>m>>s>>t;
    for(long long i=0;i<m;i++) cin>>a[i]>>b[i]>>l[i];
    for(long long i=0;i<m;i++) edge[a[i]].push_back(make_pair(b[i],l[i]));
    dijkstra(s,d1);
    for(long long i=1;i<=n;i++) edge[i].clear();
    for(long long i=0;i<m;i++) edge[b[i]].push_back(make_pair(a[i],l[i]));
    dijkstra(t,d2);
    for(long long i=1;i<=n;i++) edge[i].clear();
    for(long long i=0;i<m;i++){
        if(d1[a[i]]+l[i]+d2[b[i]]==d1[t]){
            edge[a[i]].push_back(make_pair(b[i],i));
            edge[b[i]].push_back(make_pair(a[i],i));
        }
    }
    tarjan(s);
    for(long long i=0;i<m;i++){
        if(bridge[i]) cout<<"YES"<<endl;
        else if(d1[t]-d1[a[i]]-d2[b[i]]-1>0) cout<<"CAN "<<l[i]-(d1[t]-d1[a[i]]-d2[b[i]]-1)<<endl;
        else cout<<"NO"<<endl;
    }
    return 0;
}

最小生成树

Prim 算法

Prim算法基于贪心,我们每次总是选出一个离生成树距离最小的点去加入生成树,最后实现最小生成树
#include<bits/stdc++.h>
using namespace std;
#define inf 123456789
#define maxn 5005
#define maxm 200005
struct edge{
	int v,w,next;
}e[maxm<<1];
int head[maxn],dis[maxn],cnt,n,m,tot,now=1,ans;
bool vis[maxn];
void add(int u,int v,int w){
	e[++cnt].v=v;
	e[cnt].w=w;
	e[cnt].next=head[u];
	head[u]=cnt;
}
void init(){
    cin>>n>>m;
    for(re int i=1,u,v,w;i<=m;++i){
        cin>>u>>v>>w;
        add(u,v,w),add(v,u,w);
    }
}
int prim(){
	for(re int i=2;i<=n;++i) dis[i]=inf;
	for(re int i=head[1];i;i=e[i].next) dis[e[i].v]=min(dis[e[i].v],e[i].w);
    while(++tot<n){
        int minn=inf;
        vis[now]=1;
        for(re int i=1;i<=n;++i){
            if(!vis[i]&&minn>dis[i]){
                minn=dis[i];
				now=i;
            }
        }
        ans+=minn;
        for(re int i=head[now];i;i=e[i].next){
        	re int v=e[i].v;
        	if(dis[v]>e[i].w&&!vis[v]) dis[v]=e[i].w;
		}
    }
    return ans;
}
int main(){
    init();
    printf("%d",prim());
    return 0;
}

Kruskal 算法

#include<bits/stdc++.h>
using namespace std;
struct Edge{
	int u,v,w;
}edge[200005];
int fa[5005],n,m,ans,eu,ev,cnt;
bool cmp(Edge a,Edge b){
    return a.w<b.w;
}
int find(int x){
    while(x!=fa[x]) x=fa[x]=fa[fa[x]];
    return x;
}
il void kruskal(){
    sort(edge,edge+m,cmp);
    for(int i=0;i<m;i++){
        eu=find(edge[i].u), ev=find(edge[i].v);
        if(eu==ev)  continue;
        ans+=edge[i].w;
        fa[ev]=eu;
        if(++cnt==n-1) break;
    }
}
int main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++) fa[i]=i;
    for(int i=0;i<m;i++) edge[i].u=read(),edge[i].v=read(),edge[i].w=read();
    kruskal();
    printf("%d",ans);
    return 0;
}

灌水

     /______ a
    /      /
   /______ b
  /       /
o/______ c
\        \
 \ ______ d 
  \        \
   \_______ e

最优比率生成树

∑ vi
----- >= mid
∑ ci
∑ (vi-mid*ci) >= 0
posted @ 2024-02-03 10:33  Firepaw喵喵喵  阅读(12)  评论(0)    收藏  举报