• 最短路专题总结


    题目链接:kuangbin专题

    dijk: 1 7 4 10 3 2 16
    spfa:14 12 13 5 15 18
    floyd:8 6 9
    差分约束:19 11

    最后剩下一个第17题,涉及网络流,留在网络流专题做。
    这是我做完一遍之后觉得比较好的做题顺序,一个一个知识点学习,每个知识点大致上由易到难,相同类型题放在一起加深理解。

    A - Til the Cows Come Home      POJ - 2387

    题解:就是很裸的最短路。可以拿来试板子。

    题解:最短路的变形。

    容易得到松弛条件:dis[ i ] = min(dis[ i ], max(dis[ k ], arr[ k ][ j ]))

    dis数组所代表的意义改变(代表s点到其他点的所经过的最短路径中的最大边的最小值

    这里顺便说说优先队列的意义,因为每次都是拿所有花费最小的那个点去松弛其他点,

    这样就保证了起点到其他点的距离是当前最小。(这个是有具体的证明的,但是我比较懒)

    之后这道题是求最大里的最小,既然取最小的点来松弛已经保证了最小,那么只需要记录最大即可。

    这里如果难理解的话就这么记吧,松弛所保证的是最终所要的

    比如最大的最小值,那么松弛所保证的就是最小值,那么就只需要 取最大值来比即可。

    之后输出格式需要注意,每个例子后面总是会有一行空行

    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <algorithm>
    #include <queue>
    #include <cmath>
    #include <vector>
    #define ll long long
    #define pii pair<double, int>
    
    const int inf = 0x3f3f3f3f;
    const int mod = 1e9+7;
    const int maxn = 2e5+7;
    using namespace std;
    struct node {int to,next;double w;} edge[maxn];
    int head[maxn], cnt, vis[maxn];;
    double dis[maxn];
    int n, m, s, t;
    
    void init() {
        memset(head,-1,sizeof(head));
        memset(dis,0x3f,sizeof(dis));
        memset(vis,0,sizeof(vis));
        cnt = 0;
    }
    
    void add_edge(int u,int v,double w) {
        edge[cnt].to = v;
        edge[cnt].w = w;
        edge[cnt].next = head[u];
        head[u] = cnt ++;
    }
    
    void dijkstra()
    {
        priority_queue<pii,vector<pii>,greater<pii> > q;//从小到大
        dis[s] = 0; q.push({dis[s],s});
        while(!q.empty()) {
            int now = q.top().second;
            q.pop();
            if(vis[now]) continue;
            vis[now] = 1;
            for(int i = head[now]; i != -1; i = edge[i].next) {
                int v = edge[i].to;
                if(dis[v] > max(dis[now], edge[i].w)) {
                    dis[v] = max(dis[now], edge[i].w);
                    q.push({dis[v],v});
                }
            }
        }
    }
    
    
    int main()
    {
        int num = 0;
        while (~scanf("%d", &n) && n) {
            init();
            int x[maxn], y[maxn];
            for(int i = 1; i <= n; ++i) {
                dis[i] = inf;
                scanf("%d%d",&x[i], &y[i]);
                for (int j = 1; j < i; ++j) {
                    double w = sqrt((x[i] - x[j]) * (x[i] - x[j]) + (y[i] - y[j]) * (y[i] - y[j]));
                    add_edge(i, j, w), add_edge(j, i, w);
                }
            }
            s = 1, t = 2;//s起点,t终点
            dijkstra();
            printf("Scenario #%d\n", ++num);
            printf("Frog Distance = %.3f\n\n", dis[t]);
        }
    }
    View Code

    C - Heavy Transportation    POJ - 1797

    题解:这道题与上一道题类似。但是所求的变成了最小的最大值。

    松弛条件:dis[ i ] = max(dis[ i ], min(dis[ k ], arr[ k ][ j ]))

    那么就要每次取最大的来松弛,所以dis数组初始化都为0,而不是INF,之后将s的dis变为INF,

    松弛的时候就取最小值。

    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <algorithm>
    #include <queue>
    #include <cmath>
    #include <vector>
    #define ll long long
    #define pii pair<int, int>
    
    const int inf = 0x3f3f3f3f;
    const int mod = 1e9+7;
    const int maxn = 2e5+7;
    using namespace std;
    struct node {int to,next,w;} edge[maxn];
    int head[maxn], cnt, vis[maxn];;
    int dis[maxn];
    int n, m, s, t;
    
    void init() {
        memset(head,-1,sizeof(head));
        memset(dis,0,sizeof(dis));
        memset(vis,0,sizeof(vis));
        cnt = 0;
    }
    
    void add_edge(int u,int v,int w) {
        edge[cnt].to = v;
        edge[cnt].w = w;
        edge[cnt].next = head[u];
        head[u] = cnt ++;
    }
    
    void dijkstra()
    {
        priority_queue<pii,vector<pii>,less<pii> > q;//从小到大
        dis[s] = inf; q.push({dis[s],s});
        while(!q.empty()) {
            int now = q.top().second;
            q.pop();
            if(vis[now]) continue;
            vis[now] = 1;
            for(int i = head[now]; i != -1; i = edge[i].next) {
                int v = edge[i].to;
                if(dis[v] < min(dis[now], edge[i].w)) {
                    dis[v] = min(dis[now], edge[i].w);
                    q.push({dis[v],v});
                }
            }
        }
    }
    
    int main()
    {
        int T;
        scanf("%d", &T);
        for (int z = 1; z <= T; ++z) {
            init();
            scanf("%d%d", &n, &m);
            for(int i = 1; i <= m; ++i) {
                int u, v, w;
                scanf("%d%d%d",&u, &v, &w);
                add_edge(u, v, w), add_edge(v, u, w);
            }
            s = 1, t = n;//s起点,t终点
            dijkstra();
            printf("Scenario #%d:\n", z);
            printf("%d\n\n", dis[t]);
        }
    }
    View Code

    题解:这题算是个技巧吧。就是建反图求来回路径,并将正图反图存到两个图里,跑两遍dij。

    仔细想想也是好想,如果存在一条1到n的路径,那么反着建就是从n到1。这就算是涨个知识。

    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <algorithm>
    #include <queue>
    #include <cmath>
    #include <vector>
    #define ll long long
    #define pii pair<int, int>
    
    const int inf = 0x3f3f3f3f;
    const int mod = 1e9+7;
    const int maxn = 2e5+7;
    using namespace std;
    struct node {int to,next;int w;} edge[2][maxn];
    int head[2][maxn], cnt, vis[maxn];
    int dis[2][maxn];
    int n, m, s, t;
    
    void init() {
        memset(head,-1,sizeof(head));
        memset(dis,0x3f,sizeof(dis));
        memset(vis,0,sizeof(vis));
        cnt = 0;
    }
    
    void add_edge(int u,int v,int w,int num) {
        edge[num][cnt].to = v;
        edge[num][cnt].w = w;
        edge[num][cnt].next = head[num][u];
        head[num][u] = cnt ++;
    }
    
    void dijkstra(int num)
    {
        memset(&dis[num],inf,sizeof(dis)/2);
        memset(vis,0,sizeof(vis));
        priority_queue<pii,vector<pii>,greater<pii> > q;//从小到大
        dis[num][s] = 0; q.push({dis[num][s],s});
        while(!q.empty()) {
            int now = q.top().second;
            q.pop();
            if(vis[now]) continue;
            vis[now] = 1;
            for(int i = head[num][now]; i != -1; i = edge[num][i].next) {
                int v = edge[num][i].to;
                if(dis[num][v] > dis[num][now] + edge[num][i].w) {
                    dis[num][v] = dis[num][now] + edge[num][i].w;
                    q.push({dis[num][v],v});
                }
            }
        }
    }
    
    
    int main()
    {
        while (cin >> n >> m >> t) {
            init();
            for(int i = 1; i <= m; ++i) {
                int u, v, w;
                cin >> u >> v >> w;
                add_edge(u, v, w, 0);
                add_edge(v, u, w, 1); //反相建边
            }
            s = t;//s起点
            dijkstra(0);
            dijkstra(1);
            int ans = 0;
            for (int i = 1; i <= n; ++i) {
                ans = max(ans, dis[0][i]+dis[1][i]);
            }
            cout << ans << endl;
        }
    }
    View Code

    E - Currency Exchange   POJ - 1860

    题解:算是个找环的裸题。因为找的是正环,所以dis一开始初始化为0。

    #include<stdio.h>
    #include<iostream>
    #include<algorithm>
    #include<string.h>
    #include<vector>
    #include<cmath>
    #include<string>
    #include<map>
    #include<queue>
    using namespace std;
    typedef long long ll;
    const int INF = 0x3f3f3f3f; // 2122219134
    const int maxn = 500 + 10;
    const int maxm = 100000 + 10;
    struct E {
        int to, nex;
        double r, c;
    }edge[maxn*2];
    
    int tot,n,m,s;
    int head[maxn], cnt[maxn];
    double dis[maxn],v;
    bool vis[maxn], cir[maxn];
    
    void dfs(int u)
    {
        cir[u] = true; //cir用于标记是否被负环影响
        for(int i = head[u]; ~i; i = edge[i].nex) {
            int v = edge[i].to;
            if (!cir[v]) dfs(v);
        }
    }
    
    void init(){
        memset(cnt, 0, sizeof(cnt));//cnt[i]记录i入队的次数
        memset(dis, 0, sizeof(dis));//初始化dis
        memset(head, -1, sizeof(head));
        memset(vis, 0, sizeof(vis));
        memset(cir, 0, sizeof(cir));
        tot = 0;
    }
    
    
    inline void add_edge(int u, int v, double r, double c) {
        edge[tot].to = v;
        edge[tot].r = r;
        edge[tot].c = c;
        edge[tot].nex = head[u];
        head[u] = tot++;
    }
    
    bool bfs_spfa()
    {
        dis[s] = v;
        queue<int> q;
        q.push(s);
        vis[s] = 1;
        cnt[s] = 1;
        while (!q.empty()) {
            int u = q.front();
            q.pop();
            vis[u] = 0;
            for(int i = head[u]; i != -1; i = edge[i].nex){
                int v = edge[i].to;
                if(dis[v] < (dis[u] - edge[i].c) * edge[i].r) {
                    dis[v] = (dis[u] - edge[i].c) * edge[i].r;
                    if (!vis[v]) {
                        q.push(v);
                        vis[v] = 1;
                        ++cnt[v];
                        if(cnt[v] >= n)  return 1;
                    }
                }
            }
        }
        return 0;
    }
    
    int main(){
        ios::sync_with_stdio(0);
        cin.tie(0);
        while (cin >> n >> m >> s >> v) {
            init();
            for (int i = 0; i < m; ++i) {
                int u, v;
                double rab, rba, cab, cba;
                cin >> u >> v >> rab >> cab >> rba >> cba;
                add_edge(u, v, rab, cab);
                add_edge(v, u, rba, cba);
            }
            if (bfs_spfa()) cout << "YES" << endl;
            else cout << "NO" << endl;
    
        }
        return 0;
    }
    View Code

    题解:跟上一题差不多。但是找的是负环,dis要初始化为inf。

    #include<stdio.h>
    #include<iostream>
    #include<algorithm>
    #include<string.h>
    #include<vector>
    #include<cmath>
    #include<string>
    #include<map>
    #include<queue>
    using namespace std;
    typedef long long ll;
    #define INF 0x3f3f3f3f
    struct node {
        ll id;
        ll d;
        ll next_s;
    }side[6000];
    ll cnt,n,head[501],flag[501],dis[501],cn[501];
    ll book;
    void init(){
        memset(head,-1,sizeof(head));
        cnt=0;
    }
     
    void add(ll x,ll y,ll z){
        side[cnt].id=y;
        side[cnt].d=z;
        side[cnt].next_s=head[x];
        head[x]=cnt++;
    }
     
    ll bfs_spfa(){
        memset(cn,0,sizeof(cn));//cn[i]记录i入队的次数 
        memset(dis,INF,sizeof(dis));//初始化dis 
        dis[1]=0;
        queue<ll> p;
        //压入那个点,看dis的含义 
        p.push(1);
        flag[1]=1;
        while(p.size()){
            ll t=p.front();
            p.pop();
            flag[t]=0;
            for(int i=head[t];i!=-1;i=side[i].next_s){
                if(dis[side[i].id]>dis[t]+side[i].d){
                    dis[side[i].id]=dis[t]+side[i].d;
                    if(!flag[side[i].id]){
                        p.push(side[i].id);
                        flag[side[i].id]=1;
                        cn[side[i].id]++;
                        //接下来和求单源最短路的不同的地方 
                        if(cn[side[i].id]>=n){  //关键不能是大于,是大于等于或等于 
                            return 1;
                        }
                    }
                }
            }
        }
        return 0;
    }
     
    int main(){
        ll f;
        ios::sync_with_stdio(0);
        cin.tie(0);
        cin>>f;
        
        while(f--){
            memset(dis,INF,sizeof(dis));
            memset(flag,0,sizeof(flag));
            init();
            ll m,w;
            cin>>n>>m>>w;
            for(int i=0;i<m;i++){
                ll x,y,z;
                cin>>x>>y>>z;
                add(x,y,z);
                add(y,x,z);
            }
            for(int i=0;i<w;i++){
                ll x,y,z;
                cin>>x>>y>>z;
                add(x,y,z*(-1));
            }
            book=bfs_spfa();
            if(book==1){
                cout<<"YES"<<endl;
            }
            else cout<<"NO"<<endl;
        }
        return 0;
    }
    View Code

    题解:这题主要是题面比较恶心,就是求1到各个点的最短路的最大值。就是个裸的dij

    View Code

    H - Cow Contest    POJ - 3660

    题解:这一题我一开始想用并查集写的,但是放在最短路专题,就去网上看了一眼题解。

    这题是Floyd传递闭包。因为题目保证没有矛盾关系,所以一只牛如果跟其他n-1只牛有关系的话,那么这只牛的排名就确定了。

    传递闭包:若A->B, B->C通过传递闭包可以得到A->C(不懂得可以去看看离散数学

    所以先经过Floyd处理关系。之后统计每只牛的关系数量。等于n-1那么就确定了排名。

    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <algorithm>
    #include <queue>
    #include <cmath>
    #include <vector>
    #define ll long long
    #define pii pair<int, int>
    using namespace std;
    
    const int inf = 0x3f3f3f3f;
    const int N = 1e3+10;
    //struct node {
    //    int to,next,w;
    //} edge[2][maxn];
    //int head[2][maxn], cnt[2], vis[maxn];
    //int dis[2][maxn];
    int n, m, s, t;
    int g[N][N];
    
    inline ll read()
    {
        ll x = 0, f = 1;
        char ch = getchar();
        while (ch<'0' || ch > '9') {
            if (ch == '-') f = -1;
            ch = getchar();
        }
        while ( ch >= '0' && ch <= '9') {
            x = x * 10 + ch - '0';
            ch = getchar();
        }
        return x * f;
    }
    
    void init()
    {
        for (int i = 1; i <= n; ++ i)
            for (int j = 1; j <= n; ++ j)
                g[i][j] = inf;
    }
    
    void Floyd()
    {
        for (int k = 1; k <= n; ++ k) {
            for (int i = 1; i <= n; ++ i) {
                if (g[i][k] == inf) continue;
                for (int j = 1; j <= n; ++ j) {
                    if (g[k][j] == inf) continue;
                    if (g[i][k] == g[k][j])
                        g[i][j] = g[k][j];
                }
            }
        }
    }
    
    int main()
    {
        while (~scanf("%d%d", &n, &m)) {
            init();
            for(int i = 1; i <= m; ++i) {
                int u, v;
                u = read(), v = read();
                g[u][v] = 1, g[v][u] = -1;
    //            add_edge(u, v, w, 0);
    //            add_edge(v, u, w, 1); //反相建边
            }
    //        s = 1;//s起点
    //        dijkstra(0), dijkstra(1);
    //        ll ans = 0;
            Floyd();
            int res = 0;
            for (int i = 1; i <= n; ++i) {
                int temp = 0;
                for (int j = 1; j <= n; ++j) {
                    if (g[i][j] != inf) ++ temp;
                }
                if (temp == n - 1) ++ res;
            }
            printf("%d\n", res);
        }
    }
    View Code

    I - Arbitrage   POJ - 2240

    题解:这题跟之前那道换钱差不多。就是需要先用map来hash字符之后就一样的解法。

    #include <iostream>
    #include <cstdio>
    #include <cmath>
    #include <cstring>
    #include <string>
    #include <map>
    using namespace std;
    const int N = 50 + 10;
    const int M = 1000000 + 10;
    double dist[N];
    int head[N];
    int n, m, s, tot;
    
    struct E {
        int to, next;
        double r;
    }edge[M*2];
    
    inline void add_edge(int u, int v, double r) {
        edge[tot].to = v;
        edge[tot].r = r;
        edge[tot].next = head[u];
        head[u] = tot++;
    }
    
    bool BellmanFord() {
        dist[s] = 1;
        for (int i = 0; i < n - 1; ++ i){
            for (int u = 1; u <= n; ++ u){
                if (dist[u] == 0) continue;
                for (int k = head[u]; ~k; k = edge[k].next)
                    if (dist[edge[k].to] < dist[u] * edge[k].r)
                    dist[edge[k].to] = dist[u] * edge[k].r;
            }
        }
        for (int u = 1; u <= n; ++ u){
            if (dist[u] == 0) continue;
            for (int k = head[u]; ~k; k = edge[k].next)
                    if (dist[edge[k].to] < dist[u] * edge[k].r)
                        return false;
        }
        return true;
    }
    
    void init() {
        tot = 0;
        memset(head, -1, sizeof(head));
        memset(dist, 0, sizeof(dist));
    }
    
    int main() {
        ios::sync_with_stdio(0);
        cin.tie(0);
    //    freopen("in.txt","r",stdin); //输入重定向,输入数据将从in.txt文件中读取
    //    freopen("out.txt","w",stdout); //输出重定向,输出数据将保存在out.txt文件中
        int cnt = 0;
        while (cin >> n && n) {
            init();
            map<string, int> dic;
            for (int i = 1; i <= n; ++i) {
                string temp;
                cin >> temp;
                dic[temp] = i;
            }
            cin >> m;
            for (int i = 0; i < m; ++i) {
                string u, v;
                double rate;
                cin >> u >> rate >> v;
                add_edge(dic[u], dic[v], rate);
            }
            cout << "Case " << ++cnt << ": ";
            s = 1;
            if (!BellmanFord()) cout << "Yes" << endl;
            else cout << "No" << endl;
        }
        return 0;
    }
    View Code

    题解:这题跟之前正反建图的题目差不多。不过这道题卡了cin取消同步。要用scanf,或者读入挂

    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <algorithm>
    #include <queue>
    #include <cmath>
    #include <vector>
    #define ll long long
    #define pii pair<int, int>
    using namespace std;
    
    const int inf = 0x3f3f3f3f;
    const int maxn = 1e6+10;
    struct node {
        int to,next,w;
    } edge[2][maxn];
    int head[2][maxn], cnt[2], vis[maxn];
    int dis[2][maxn];
    int n, m, s, t;
    
    inline ll read()
    {
        int x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    
    void init() {
        for (int i = 0; i < 2; ++i)
            for (int j = 1; j <= n; ++j)
                head[i][j] = 0;
        cnt[0] = cnt[1] = 0;
    }
    
    void add_edge(int u,int v,int w,int num) {
        edge[num][++cnt[num]].to = v;
        edge[num][cnt[num]].w = w;
        edge[num][cnt[num]].next = head[num][u];
        head[num][u] = cnt[num];
    }
    
    void dijkstra(int num)
    {
        for (int i = 1; i <= n; ++i) dis[num][i] = inf;
        for (int i = 1; i <= n; ++i) vis[i] = 0;
        priority_queue<pii,vector<pii>,greater<pii> > q;//从小到大
        dis[num][s] = 0; q.push(make_pair(dis[num][s],s));
        //num = 1;
        while(!q.empty()) {
            int now = q.top().second;
            q.pop();
            if(vis[now]) continue;
            vis[now] = 1;
            for(int i = head[num][now]; i; i = edge[num][i].next) {
                int v = edge[num][i].to;
                if (vis[v]) continue;
                if(dis[num][v] > dis[num][now] + edge[num][i].w) {
                    dis[num][v] = dis[num][now] + edge[num][i].w;
                    q.push(make_pair(dis[num][v],v));
                }
            }
        }
    }
    
    
    int main()
    {
        int T;
        T = read();
        while (T--) {
            n =  read(), m = read();
            init();
            for(int i = 1; i <= m; ++i) {
                int u, v, w;
                u = read(), v = read(), w = read();
                add_edge(u, v, w, 0);
                add_edge(v, u, w, 1); //反相建边
            }
            s = 1;//s起点
            dijkstra(0), dijkstra(1);
            ll ans = 0;
            for (int i = 1; i <= n; ++i) {
                ans += dis[0][i] + dis[1][i];
            }
            printf("%lld\n", ans);
        }
    }
    View Code

    L - Subway    POJ - 2502

    题解:这道题就有的说了。毕竟我是第一次自己建图这样的。说一下建图思路吧。

    hash站点,起点和重点。

    每一条地铁线里的站前后建边,权值为欧几里得距离 除以 40km/h。

    所有的点之间相互建边,表示人步行的方案,权值为欧几里得距离 除以 10km/h。

    剩下的事儿就交给最短路吧。

    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <algorithm>
    #include <math.h>
    #include <queue>
    #include <iomanip>
    #include <vector>
    #define ll long long
    #define pii pair<double, int>
    
    const int inf = 0x3f3f3f3f;
    const int mod = 1e9+7;
    const int maxn = 2e5+7;
    using namespace std;
    struct node {int to,next;double w;} edge[maxn];
    struct point{double x, y;} poi[maxn];
    int head[maxn], tot;
    double dis[maxn];
    bool vis[maxn];
    int n, m, s, t;
    
    inline ll read()
    {
        ll x = 0, f = 1;
        char ch = getchar();
        while (ch<'0' || ch > '9') {
            if (ch == '-') f = -1;
            ch = getchar();
        }
        while ( ch >= '0' && ch <= '9') {
            x = x * 10 + ch - '0';
            ch = getchar();
        }
        return x * f;
    }
    
    void init()
    {
        memset(head,-1,sizeof(head));
        for (int i = 0; i < maxn; ++ i) dis[i] = (double)(1<<30);
        memset(vis,0,sizeof(vis));
        tot = 0;
    }
    
    void add_edge(int u,int v, double w)
    {
        edge[tot].to = v;
        edge[tot].w = w;
        edge[tot].next = head[u];
        head[u] = tot ++;
    }
    
    double cal(int a, int b)
    {
        //cout << sqrt(((poi[a].x-poi[b].x)*(poi[a].x-poi[b].x) + 1.0*(poi[a].y-poi[b].y)*(poi[a].y-poi[b].y))) << endl;
        return sqrt(((poi[a].x-poi[b].x)*(poi[a].x-poi[b].x) + 1.0*(poi[a].y-poi[b].y)*(poi[a].y-poi[b].y)));
    }
    
    void dijkstra(int s)
    {
        priority_queue<pii,vector<pii>,greater<pii> > q;//从小到大
        dis[s] = 0; q.push({dis[s],s});
        while(!q.empty()) {
            int now = q.top().second;
            q.pop();
            if(vis[now]) continue;
            vis[now] = 1;
            for(int i = head[now]; i != -1; i = edge[i].next) {
                int v = edge[i].to;
                if(dis[v] > dis[now] + edge[i].w) {
                    dis[v] = dis[now] + edge[i].w;
                    q.push({dis[v],v});
                }
            }
        }
    }
    
    int main()
    {
        scanf("%lf%lf%lf%lf",&poi[1].x,&poi[1].y,&poi[2].x,&poi[2].y);
        init();
        double x, y;
        int cnt = 2;
        while (~scanf("%lf%lf", &x, &y)) {
            poi[++ cnt].x = x, poi[cnt].y = y;
            while (scanf("%lf%lf", &x, &y)) {
                if (x == -1 && y == -1) break;
                poi[++ cnt].x = x, poi[cnt].y = y;
                add_edge(cnt-1, cnt, cal(cnt-1, cnt)/40000),
                add_edge(cnt, cnt-1, cal(cnt-1, cnt)/40000);
            }
        }
    
        for (int i = 1; i <= cnt; ++ i) {
            for (int j = i + 1; j <= cnt; ++ j)
                add_edge(i, j, cal(i, j)/10000), add_edge(j, i, cal(j, i)/10000);
        }
        dijkstra(1);
        cout << fixed << setprecision(0) << (dis[2]*60.0) << endl;
        //printf("%.0lf\n", dis[2]*60.0);
        return 0;
    }
    View Code

    题解:这道题的限制挺有趣的。建边倒也是非常的明显。

    因为限制了交易等级那么,我们最终又一定是要跟酋长交易的,假设酋长的等级为k,题目限制登记为x

    那么我们就可以枚举区间[k-x, k], [k-x+1, k+1] ......[k, k+x],

    如果枚举人的等级不在这个范围内那么就vis标记为1,表示他不会进入最短路。

    不要忘了每次枚举都要初始化dis还有vis,

    初始化dis的原因是因为下一次枚举可能会调用上次在区间里而这次不在区间里的人的dis

    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <algorithm>
    #include <queue>
    #include <vector>
    #define ll long long
    #define pii pair<int, int>
    
    const int inf = 0x3f3f3f3f;
    const int mod = 1e9+7;
    const int maxn = 2e4+7;
    using namespace std;
    struct node {int to,w,next;} edge[maxn];
    int head[maxn], cnt;
    int dis[maxn], vis[maxn];
    
    inline ll read()
    {
        ll x = 0, f = 1;
        char ch = getchar();
        while (ch<'0' || ch > '9') {
            if (ch == '-') f = -1;
            ch = getchar();
        }
        while ( ch >= '0' && ch <= '9') {
            x = x * 10 + ch - '0';
            ch = getchar();
        }
        return x * f;
    }
    
    void init()
    {
        memset(head,-1,sizeof(head));
        memset(dis,0x3f,sizeof(dis));
        memset(vis,0,sizeof(vis));
        cnt = 0;
    }
    
    void add_edge(int u,int v,int w)
    {
        edge[cnt].to = v;
        edge[cnt].w = w;
        edge[cnt].next = head[u];
        head[u] = cnt ++;
    }
    
    void dijkstra(int s)
    {
        priority_queue<pii,vector<pii>,greater<pii> > q;//从小到大
        dis[s] = 0; q.push({dis[s],s});
        while(!q.empty()) {
            int now = q.top().second;
            q.pop();
            if(vis[now]) continue;
            vis[now] = 1;
            for(int i = head[now]; i != -1; i = edge[i].next) {
                int v = edge[i].to;
                if(dis[v] > dis[now] + edge[i].w) {
                    dis[v] = dis[now] + edge[i].w;
                    q.push({dis[v],v});
                }
            }
        }
    }
    
    
    int main()
    {
        int n, x, m;
        while (~scanf("%d%d",&m,&n)) {
            init();
            int p, l, x;
            int t, v;
            int rk[maxn] = {0};
            for(int i = 1; i <= n; i ++) {
                scanf("%d %d %d", &p, &l, &x);
                add_edge(0, i, p);
                rk[i] = l;
                while (x--) {
                    scanf("%d %d", &t, &v);
                    add_edge(t, i, v);
                }
            }
            int temp = inf;
            int minprice = inf;
            int maxlv;
            for(int i = 0; i <= m; i++)
            {
                memset(dis,0x3f,sizeof(dis));
                memset(vis, 0, sizeof(vis));
                maxlv = rk[i];  //把当前物品的等级暂时看做最高等级
                for(int j = 1; j <= n; j++)
                    if(rk[1]-m+i>rk[j]||rk[1]+i<rk[j]) vis[j] = 1;
                dijkstra(0);
                minprice = min(minprice, dis[1]); //维护最小值
            }
            cout << minprice << endl;
        }
        return 0;
    }
    View Code

    N - Tram    POJ - 1847

    题解:这题没啥好讲的,就是把边权赋值为1,跑一边 dij 就好了。

    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <algorithm>
    #include <queue>
    #include <cmath>
    #include <string>
    #include <vector>
    #define ll long long
    #define pii pair<int, int>
    
    const int inf = 0x3f3f3f3f;
    const int mod = 1e9+7;
    const int maxn = 2e5+7;
    using namespace std;
    struct node {int to,next;int w;} edge[maxn];
    int head[maxn], cnt, vis[maxn];;
    int dis[maxn], dis_to[maxn];
    int n, s, t;
    
    int input()
    {
        char ch;
        ch=getchar();
        while( (ch<'0'||ch>'9')&&ch!='x' )ch=getchar();
        if(ch=='x')return inf;
        int ret=0;
        while(ch>='0'&&ch<='9')
        {
            ret*=10;
            ret+=ch-'0';
            ch=getchar();
        }
        return ret;
    }
    
    void init() {
        memset(head,-1,sizeof(head));
        memset(dis,0x3f,sizeof(dis));
        memset(vis,0,sizeof(vis));
        cnt = 0;
    }
    
    void add_edge(int u,int v,int w) {
        edge[cnt].to = v;
        edge[cnt].w = w;
        edge[cnt].next = head[u];
        head[u] = cnt ++;
    }
    
    void dijkstra()
    {
        priority_queue<pii,vector<pii>,greater<pii> > q;//从小到大
        dis[s] = 0; q.push({dis[s],s});
        while(!q.empty()) {
            int now = q.top().second;
            q.pop();
            if(vis[now]) continue;
            vis[now] = 1;
            for(int i = head[now]; i != -1; i = edge[i].next) {
                int v = edge[i].to;
                if(dis[v] > dis[now] + edge[i].w) {
                    dis[v] = dis[now] + edge[i].w;
                    q.push({dis[v],v});
                }
            }
        }
    }
    
    
    int main()
    {
        n = input(), s = input(), t = input();
        init();
        for(int i = 1; i <= n; ++ i) {
            int m = input();
            for (int j = 0; j < m; ++ j) {
                int temp = input();
                add_edge(i, temp, j != 0);
            }
        }
        dijkstra();
        cout << ((dis[t] == inf)?(-1):(dis[t])) << endl;
    
        return 0;
    }
    View Code

    O - Extended Traffic     LightOJ - 1074

    题解:这题挺有趣的。我以为负权环是不能求最短路的。但是其实没被负权环影响的边还是可以求出最短路的。

    同时学了如何处理被负权环影响的边。挺好的一道题。

    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <algorithm>
    #include <math.h>
    #include <queue>
    #include <iomanip>
    #include <vector>
    #define ll long long
    #define pii pair<int, int>
    
    const int inf = 0x3f3f3f3f;
    const int mod = 1e9+7;
    const int maxn = 2e5+7;
    using namespace std;
    struct node {int to,nex,w;} edge[maxn];
    int head[maxn], tot;
    int dis[maxn];
    bool vis[maxn], cir[maxn];
    int cnt[maxn];
    int n, m;
    
    inline ll read()
    {
        ll x = 0, f = 1;
        char ch = getchar();
        while (ch<'0' || ch > '9') {
            if (ch == '-') f = -1;
            ch = getchar();
        }
        while ( ch >= '0' && ch <= '9') {
            x = x * 10 + ch - '0';
            ch = getchar();
        }
        return x * f;
    }
    
    void init()
    {
        memset(head, -1, sizeof(head));
        memset(dis, inf, sizeof(dis));
        memset(vis, 0, sizeof(vis));
        memset(cir, 0, sizeof(cir));
        memset(cnt, 0, sizeof(cnt));
        tot = 0;
    }
    
    void add_edge(int u,int v, int w)
    {
        edge[tot].to = v;
        edge[tot].w = w;
        edge[tot].nex = head[u];
        head[u] = tot ++;
    }
    
    
    void dfs(int u)
    {
        cir[u] = true; //cir用于标记是否被负环影响
        for(int i = head[u]; ~i; i = edge[i].nex) {
            int v = edge[i].to;
            if (!cir[v]) dfs(v);
        }
    }
    
    void bfs_spfa(int s)
    {
        queue<int> q;
        q.push(s);
        dis[s] = 0;
        vis[s] = 1;
        //cnt[s] = 1;
        while (!q.empty()) {
            int u = q.front();
            q.pop();
            vis[u] = 0;
            for(int i = head[u]; ~i; i = edge[i].nex){
                int v = edge[i].to, w = edge[i].w;
    
                if(cir[v]) continue; //cir用于标记是否被负环影响
                //cout << u << "---" << v <<  "    " << "  dis[u]:" << dis[u] << "  w" << w << "  dis[v]:" << dis[v] << endl;
                if(dis[v] > dis[u] + w) {
    
                    dis[v] = dis[u] + w;
                    if (!vis[v]) {
                        q.push(v);
                        vis[v] = 1;
                        //接下来和求单源最短路的不同的地方
                        if(++ cnt[v] > n) dfs(v); //关键不能是大于,是大于等于
                    }
                }
            }
        }
    }
    
    int main()
    {
        int T = read();
        for (int Ti = 1; Ti <= T; ++ Ti) {
            init();
            n = read();
            int cost[n+1];
            for (int i = 1; i <= n; ++ i) cost[i] = read();
            m = read();
            for (int i = 1; i <= m; ++ i) {
                int u = read(), v = read();
                add_edge(u, v, (cost[v]-cost[u])*(cost[v]-cost[u])*(cost[v]-cost[u]));
                //cout <<(cost[v]-cost[u])*(cost[v]-cost[u])*(cost[v]-cost[u]) << endl;
            }
            bfs_spfa(1);
            int q = read();
            cout << "Case " << Ti << ":" << endl;
            for (int i = 0; i < q; ++ i) {
                int t = read();
                //cout << dis[t] << endl;
                if (dis[t] == inf || dis[t] < 3 || cir[t]) cout << "?" << endl;
                else cout << dis[t] << endl;
            }
        }
        return 0;
    }
    View Code

    P - The Shortest Path in Nya Graph   HDU - 4725

    题解:这道题重要的就是建图了。我一开始就是打算点与点之间连,

    每个层建一个虚点,之后虚点之间连边,边权为c,每层的点与所属层的虚点连边,边权为0。

    之后这些边都是双向边。但是这样就导致了同层点相互访问的话,cost就会是0了(通过虚点过去回来),显然是不行的

    只得去看了一眼题解。大致的思路跟我差不多,也是每层弄出一个虚点,虚点与每层的点相连,但是题解是连的是单向边,边权为0

    然后前后虚点之间连一条双向边,边权为c,注意这里如果层数不是连续的话是不连的

    针对每个点,将他与他所属层数上下两层的虚点相连。

    之后跑遍最短路就好了。

    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <algorithm>
    #include <queue>
    #include <cmath>
    #include <map>
    #include <vector>
    #define ll long long
    #define pii pair<int, int>
    using namespace std;
    
    const int inf = 0x3f3f3f3f;
    const int maxn = 1e6+100;
    struct node {
        int to,next,w;
    } edge[maxn];
    int head[maxn], tot, vis[maxn];
    int dis[maxn];
    int n, m, c;
    int layer[maxn];
    bool mark[maxn];
    
    inline ll read()
    {
        int x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    
    void init()
    {
        memset(layer,0,sizeof(layer));
        memset(mark,0,sizeof(mark));
        memset(head,0,sizeof(head));
        memset(dis,0x3f,sizeof(dis));
        memset(vis,0,sizeof(vis));
        tot = 0;
    }
    
    void add_edge(int u,int v,int w)
    {
        edge[++tot].to = v;
        edge[tot].w = w;
        edge[tot].next = head[u];
        head[u] = tot;
    }
    
    void dijkstra(int s)
    {
        priority_queue<pii,vector<pii>,greater<pii> > q;
        dis[s] = 0, q.push({dis[s],s});
        while(!q.empty()) {
            int u = q.top().second;
            q.pop();
            if(vis[u]) continue;
            vis[u] = 1;
            for(int i = head[u]; i; i = edge[i].next) {
                int v = edge[i].to, w = edge[i].w;
                if (vis[v]) continue;
                if(dis[v] > dis[u] + w) {
                    dis[v] = dis[u] + w;
                    q.push({dis[v],v});
                }
            }
        }
    }
    
    
    int main()
    {
    //    freopen("in.txt","r",stdin); //输入重定向,输入数据将从in.txt文件中读取
    //    freopen("out1.txt","w",stdout); //输出重定向,输出数据将保存在out.txt文件中
        int T;
        T = read();
        for (int Ti = 1; Ti <= T; ++ Ti) {
            n =  read(), m = read(), c = read();
    //        if(n<=1) {
    //            printf("Case #%d: 0\n", Ti);
    //            continue;
    //        }
            init();
            for (int i = 1; i <= n; ++ i)
                mark[layer[i] = read()] = 1;
    
            for(int i = 1; i <= m; ++i) {
                int u, v, w;
                u = read(), v = read(), w = read();
                add_edge(u, v, w), add_edge(v, u, w);
            }
            for (int i = 1; i < n; ++ i) {
                if (mark[i] && mark[i+1])
                    add_edge(n + i, n + i + 1, c), add_edge(n + i + 1, n + i, c);
            }
            for (int i = 1; i <= n; ++ i) {
                add_edge(layer[i]+n, i, 0);
                if (layer[i] > 1) add_edge(i, layer[i]-1+n, c);
                if (layer[i] < n) add_edge(i, layer[i]+1+n, c);
            }
            dijkstra(1);
            printf("Case #%d: %d\n", Ti, dis[n]>=inf?-1:dis[n]);
        }
        return 0;
    }
    View Code

    题解:这道题我根本就没看出来是最短路。思维转换真的很重要。

    题目给的三个条件就是说。

    一、点1的度为1

    二、点n的度为1

    三、1-n之间的点度相同

    之后求最短路即可。

    但是要注意满足上述三种条件的答案有两种情况。

    第一种是一条链,第二种是1和n为起点的两个自环。

    顺便学习了一下spfa求环花费。

    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<map>
    #include<cstdio>
    #include<queue>
    #include<stack>
    
    using namespace std;
    
    const int INF = 0x3f3f3f3f;
    
    int cost[305][305];
    int dis[305];
    int n;
    bool vis[305];
    
    void spfa( int start ){
        stack<int> Q;
    
        for( int i = 1; i <= n; i++ ){
            dis[i] = cost[start][i];
            if( i != start ){
                Q.push( i );
                vis[i] = true;
            }
            else{
                vis[i] = false;
            }
        }
        dis[start] = INF;
    
        while( !Q.empty() ){
            int x = Q.top(); Q.pop(); vis[x] = false;
    
            for( int y = 1; y <= n; y++ ){
                if( x == y ) continue;
                if( dis[x] + cost[x][y] < dis[y] ){
                    dis[y] = dis[x] + cost[x][y];
                    if( !vis[y] ){
                        vis[y] = true;
                        Q.push( y );
                    }
                }
            }
        }
    }
    
    int main(){
        ios::sync_with_stdio( false );
    
        while( cin >> n ){
            for( int i = 1; i <= n; i++ ){
                for( int j = 1; j <= n; j++ ){
                    cin >> cost[i][j];
                }
            }
    
            int ans, c1, cn;
            spfa( 1 );
            ans = dis[n];
            c1 = dis[1];
            spfa( n );
            cn = dis[n];
    
    
            cout << min( ans, c1 + cn ) << endl;
        }
    
        return 0;
    }
    View Code

    20.08.23 UPD:

    突然忘了还有几道题目没有补。正巧把最大流学完了。

    Q - Marriage Match IV  HDU - 3416

    这道题就是问你从S到T有几条最短路。首先的思路就是正反跑一遍最短路,因为要确认哪条边在最短路上。

    确认方法:S->u + u->v + v->T == minn

    之后直接建边跑最大流就好了。

    这里千万要注意的是,这里题目给出的虽然是无向图,但是你却要按照有向图正反建。(就是像我代码里,需要分两个vector来存边。

    不然会wa

    #pragma GCC optimize(2)
    #include <bits/stdc++.h>
    using namespace std;
    #define pii pair<int, int>
    typedef long long ll;
    const int maxn = 2e3 + 10;
    const int maxm = 2e6 + 10;
    const int inf = 0x3f3f3f3f;
    const int mod = 1e9 + 7;
    
    struct Edge{
        int to;
        int next;
        int w;
    } edge[maxm];
    
    int head[maxn], node_num;
    int cur[maxn];//当前弧优化数组
    int n, m, S, T, k;//点数,边数,源点,汇点
    int dis[maxn];//Bfs深度
    int cnt = 0;//边数
    int vis[maxn];
    vector<pii> G[maxn][2];
    int dist[maxn][2];
    
    inline int read()
    {
        int x = 0, f = 1;
        char ch = getchar();
        while (ch<'0' || ch > '9') {
            if (ch == '-') f = -1;
            ch = getchar();
        }
        while ( ch >= '0' && ch <= '9') {
            x = x * 10 + ch - '0';
            ch = getchar();
        }
        return x * f;
    }
    
    inline void init(){
        cnt = 0;
        for (int i = 0; i <= n;++ i)
            head[i] = -1, G[i][0].clear(), G[i][1].clear();
    }
    
    inline void Add_Edge(int u, int v, int w){
        edge[cnt].next = head[u];
        edge[cnt].to = v;
        edge[cnt].w = w;
        head[u] = cnt++;
    
        edge[cnt].next = head[v];
        edge[cnt].to = u;
        edge[cnt].w = 0;
        head[v] = cnt++;
    
    }
    
    inline bool Bfs(){
        for(int i = 0; i <= n; ++i)
            dis[i] = -1; //这里要根据你所建的点数来确定
        dis[S] = 0;
        queue<int> q;
        q.push(S);
        while(!q.empty()){
            int u = q.front(); q.pop();
            for(int i = head[u]; ~i ; i = edge[i].next){
                int v = edge[i].to;
                if(dis[v] == -1 && edge[i].w){//没有标记深度并且有残量
                    dis[v] = dis[u] + 1;
                    q.push(v);
                }
            }
        }
        return dis[T] != -1;
    }
    
    inline int dfs(int u, int flow) {
        if(u == T) return flow;
        int del = flow;
        for(int i = cur[u]; ~i; i = edge[i].next){
            cur[u] = edge[i].next;//当前弧优化,下次直接从cur[u]开始增广,节省时间
            int v = edge[i].to;
            if (dis[v] == dis[u] + 1 && edge[i].w > 0){//深度+1且残量大于0
                int ans = dfs(v, min(del, edge[i].w));//木桶原理
                edge[i].w -= ans;//正向弧减增广流量
                edge[i ^ 1].w += ans;//反向弧加增广流量
                del -= ans;//总流量减增广流量
                if(del == 0) break;//总流量为0则不继续增广
            }
        }
        return flow - del;//返回本次增广的流量
    }
    
    inline int Dinic() {
        int ans = 0;
        while(Bfs()) {
            for(int i = 0; i <= n; ++i) cur[i] = head[i];
            ans += dfs(S, inf);
        }
        return ans;
    }
    
    void dijkstra(int s, int pos)
    {
        for (int i = 0; i <= n; ++i) vis[i] = 0, dist[i][pos] = inf;
        priority_queue<pii,vector<pii>,greater<pii> > q;//从小到大
        dist[s][pos] = 0; q.push({dist[s][pos],s});
        while(!q.empty()) {
            int now = q.top().second;
            q.pop();
            if(vis[now]) continue;
            vis[now] = 1;
            for(int i = 0; i < G[now][pos].size(); i++) {
                int v = G[now][pos][i].first;
                int w = G[now][pos][i].second;
                if(dist[v][pos] > dist[now][pos] + w) {
                    dist[v][pos] = dist[now][pos] + w;
                    q.push({dist[v][pos],v});
                }
            }
        }
    }
    
    int main() {
        int Ti = read();
        while (Ti--) {
            n = read(), m = read();
            init();
            int px[m+1], py[m+1], pw[m+1];
            for (int i = 1,u,v,w; i <= m; ++ i) {
                u = read(), v = read(), w = read();
                px[i] = u, py[i] = v, pw[i] = w;
                G[u][0].push_back({v,w}), G[v][1].push_back({u,w});
            }
            S = read(), T = read();
            dijkstra(S, 0), dijkstra(T, 1);
            int minn = dist[S][1];
            for (int i = 1; i <= m; ++ i) {
                int u = px[i], v = py[i], w = pw[i];
                if (dist[u][0] + w + dist[v][1] == minn)
                    Add_Edge(u, v, 1);
            }
            cout << Dinic() << endl;
        }
        return 0;
    }
    View Code
  • 相关阅读:
    内层城循环应用——买衣服
    内外层循环用法
    自定义函数的应用
    少有人走的路 随笔
    拆单发货逻辑
    拆单发货-分布页
    拆单发货-主页
    SP--report存储过程
    关于C#对Xml数据解析
    C#模拟http 发送post或get请求
  • 原文地址:https://www.cnblogs.com/Vikyanite/p/13299601.html
Copyright © 2020-2023  润新知