ACM恢復訓練(一)最短路

很久沒有寫博客了,今天配好了sublime,打算正式啓航吧node

找到了以前的本身的CSP複習模板,QQ郵箱真是個神奇的地方,什麼郵件附件都能找回c++

自認爲本身不是個創新型人才,因此須要不斷學習來渴望上天一點恩賜QAQ算法

不貧了,第一個選擇複習的是圖論-最短路ide

1、dijstra算法(nlogn)學習

從新再來複習這個算法,其基本思想仍是不太難理解的優化

重點是一個dp的思想,轉移狀態dis[from]->dis[to]的變化spa

確定用if語句進行判斷code

建邊就是常規操做,而後從隊列裏面找到優先級高的blog

就是隊列裏面當下已經訪問的點中距離最小的,開始遍歷他的出邊隊列

直到隊列中再也不有點

#include<bits/stdc++.h>
using namespace std;
#define I inline
#define INF 0x7f7f7f7f
const  int N=4e5+7;
int cnt,m,n,ans,F;
int head[N],dis[N];
struct edge{
    int dist,nx,to;
} e[N];
struct node{
    int pos,dist;
    bool operator<(const node &x) const{
        return dist>x.dist;
    }
};
bool vis[N];
priority_queue<node> que;


void dij(int pos){
    for (int i=1;i<=N;i++) dis[i]=INF;
    que.push((node){pos,0});dis[pos]=0;
    while (!que.empty()){
        node tmp=que.top();que.pop();
        int x=tmp.pos;
        if (vis[x]) continue; vis[x]=true;
        for (int i=head[x];i;i=e[i].nx){
            int y=e[i].to;
            if (dis[y]>dis[x]+e[i].dist) {
                dis[y]=dis[x]+e[i].dist;
                if (!vis[y]) que.push((node){y,dis[y]});
            }
        }
    }
}
void add_edge(int x,int y,int dis){
    cnt++;e[cnt].nx=head[x],e[cnt].to=y,e[cnt].dist=dis;head[x]=cnt;
}



int main(){
    scanf("%d%d%d",&n,&m,&F);
    while (m--){
        int s,t,u;scanf("%d%d%d",&s,&t,&u);
        add_edge(s,t,u);
    }
    dij(F);
    for (int i=1;i<=n;i++)
        printf("%d ",dis[i]);
    return 0;
}
View Code

2、SPFA算法(VE)

SPFA算法就是對bellman-Ford算法的隊列優化

既然咱們對於每一個能到達的邊都要鬆弛那麼咱們能夠判斷是否在隊列裏面

來減小操做的次數,雖然沒有dij那麼強大的算法複雜度可是能夠用來判斷負權迴路

這也着實是其一大特色

#include<bits/stdc++.h>
using namespace std;
const int INF=0x7f7f7f7f;
const int N=1e5+7;
int dis[N],head[N];
struct edge{
    int nx,to,dist;
} e[N<<1];
int m,n,cnt,ans;
queue<int> que;
bool inque[N];

void add_edge(int x,int y,int dis){
    cnt++;e[cnt].dist=dis;e[cnt].nx=head[x];e[cnt].to=y;head[x]=cnt;
}
void SPFA(int pos){
    for (int i=1;i<=n;i++){
        dis[i]=INF;inque[i]=false;
    }
    que.push(pos);inque[pos]=true;dis[pos]=0;
    while (!que.empty()){
        int x=que.front();inque[x]=false;que.pop();
        for (int i=head[x];i;i=e[i].nx){
            int y=e[i].to;
            if (dis[y]>dis[x]+e[i].dist){
                dis[y]=dis[x]+e[i].dist;
                if (!inque[y]) {inque[y]=true;que.push(y);}
            }
        }
    }
}

int main(){
    scanf("%d %d",&n,&m);
    for (int i=1;i<=m;i++){
        int x,y,z;scanf("%d%d%d",&x,&y,&z);
        add_edge(x,y,z);
    }
    SPFA(1);
    for (int i=1;i<=n;i++) printf("%d ",dis[i]);
    return 0;
}
View Code

 3、差分約束算法

主要是用來解決一羣不等式同時成立的問題的狀況的
例如說:
x1-x2<=c1 這樣的話須要x1->x2連一條權值爲-c1的邊

原理來自於dis[x]<=dis[y]+w;這樣的最長路不等式

幾種狀況:
1.若是圖上存在負環(求最短路),則說明沒法知足全部不等式成立的狀況
2.求出最短路後,則是原來不等式的對應的一個解x[i]=dis[i]而且這個解是最小解且大於零

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+7;

int m,n,cnt;
int head[N],dis[N],in[N];
bool vis[N];
struct edge{
    int nx,to,dis;
} e[N];

void add_edge(int x,int y,int dis){
    cnt++;e[cnt].nx=head[x];e[cnt].to=y;e[cnt].dis=dis;head[x]=cnt;
}
bool SPFA(int pos){
    for (int i=0;i<=n;i++) {dis[i]=-1;vis[i]=false;}
    queue<int> que;que.push(pos);vis[pos]=true;dis[pos]=0;in[pos]=1;
    while (!que.empty()){
        int x=que.front();que.pop();vis[x]=false;
        for (int i=head[x];i;i=e[i].nx){
            int y=e[i].to;
            if (dis[y]<dis[x]+e[i].dis){
                dis[y]=dis[x]+e[i].dis;
                if (!vis[y]){
                    vis[y]=true;que.push(y);
                    in[y]++;
                    if (in[y]>n) return false;
                }
            }
        }
    }
    return true;
}

int main(){
    scanf("%d%d",&n,&m);
    for (int i=1;i<=m;i++){
        int x,y,z;scanf("%d%d%d",&x,&y,&z);
        add_edge(x,y,-z);
    }
    for (int i=1;i<=n;i++) add_edge(0,i,0);
    if (!SPFA(0)) {printf("NO");return 0;}
    for (int i=1;i<=n;i++) printf("%d ",dis[i]);
    return 0;
}
View Code
相關文章
相關標籤/搜索