zoukankan      html  css  js  c++  java
  • 飞行路线 题解2

    link

    DP

    直接考虑 (dp) 。定义 (dp[i][j]) 表示到 (i) 这个点用 (j) 次优惠的最短路径。

    对于 (i) 这个点,只有用与不用优惠两种情况,由此可得状态转移方程:

    (其中 (u) 表示上一个点。

    int val = min(dp[u][j] + w, dp[u][j + 1]);
    dp[u][j] = min(dp[u][j], val);
    

    然后SPFA边跑边进行更新。

    但这样会超时,只有 (90) 分(有人玄学Dijk(potato) (online) (judge) 上卡过了,但在某谷被 (hack) 了。

    代码1

    #include <queue>
    #include <vector>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    
    const int MAXN = 10005;
    const int INF = 0x3f3f3f3f;
    int Min(int x, int y) { return x < y ? x : y; }
    struct edge {
        int v, c;
        edge() {}
        edge(int V, int C) {
            v = V;
            c = C;
        }
    };
    vector<edge> mp[MAXN];
    
    void Add_Edge(int x, int y, int w) {
        mp[x].push_back(edge(y, w));
        mp[y].push_back(edge(x, w));
        return;
    }
    
    int dp[MAXN][15];
    int n, m, k, s, t;
    
    void read(int &a) {
        a = 0;
        int k = 1;
        char s = getchar();
        while (s < '0' || s > '9') {
            if (s == '-')
                k = -1;
            s = getchar();
        }
        while (s >= '0' && s <= '9') {
            a = (a << 3) + (a << 1) + s - '0';
            s = getchar();
        }
        a *= k;
        return;
    }
    
    void write(int x) {
        if (x < 0) {
            putchar('-');
            x = -x;
        }
        if (x > 9)
            write(x / 10);
        putchar(x % 10 + '0');
    }
    
    bool vis[MAXN];
    void DP() {
        queue<int> q;
        q.push(s);
        while (!q.empty()) { 
            int cur = q.front();
            q.pop();
            vis[cur] = 0;
            for (int i = 0; i < mp[cur].size(); i++) {
                int v = mp[cur][i].v, w = mp[cur][i].c;
                for (int j = 0; j <= k; j++) {
                    int val = Min(dp[cur][j] + w, dp[cur][j + 1]);
                    if (val < dp[v][j]) {
                        dp[v][j] = val;
                        if (!vis[v]) {
                            q.push(v);
                            vis[v] = 1;
                        }
                    }
                }
            }
        }
    }
    
    int main() {
        memset(dp, 0x3f, sizeof dp);
        read(n);
        read(m);
        read(k);
        read(s);
        read(t);
        for (int i = 1; i <= m; i++) {
            int x, y, w;
            read(x);
            read(y);
            read(w);
            Add_Edge(x, y, w);
        }
        for (int i = 0; i <= k; i++) dp[s][i] = 0;
        DP();
        int mi = INF;
        for (int i = 0; i <= k; i++) mi = Min(mi, dp[t][i]);
        write(mi);
        return 0;
    }
    

    Update:

    @C202201tanfuwen Dij版过掉了。

    每次保存点的时候,把一个点存成 (k) 个点分别保存。

    其实就是多建几个点去方便状态转移。(具体看代码吧

    #include<cstdio>
    #include<iostream>
    #include<vector>
    #include<queue>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    struct node{
    	int u;
    	int w;
    	int k;
    	node(){}
    	node(int U,int W,int K){
    		u=U,w=W,k=K;
    	}
    };
    bool operator < (node a,node b){
    	return a.w>b.w;
    }
    const int MAXN=10005;
    int dis[MAXN][15];
    vector<node> g[MAXN];
    priority_queue<node> que;
    bool vis[MAXN][15];
    int n,m,k;
    void dijkstra(int s){
    	memset(dis,0x3f,sizeof(dis));
    	dis[s][0]=0;
    	que.push(node(s,0,0));
    	while(!que.empty()){
    		int now=que.top().u;
    		int now_k=que.top().k;
    		que.pop();
    		if(vis[now][now_k]) continue;
    		vis[now][now_k]=1;
    		for(int j=0;j<g[now].size();j++){
    			int v=g[now][j].u;
    			int w=g[now][j].w;
    			if(dis[v][now_k]>dis[now][now_k]+w && !vis[v][now_k]){
    				dis[v][now_k]=dis[now][now_k]+w;
    				que.push(node(v,dis[v][now_k],now_k));
    			}
    			if(now_k+1<=k){
    				if(dis[v][now_k+1]>dis[now][now_k] && !vis[v][now_k+1]){
    					dis[v][now_k+1]=dis[now][now_k];
    					que.push(node(v,dis[v][now_k+1],now_k+1));
    				}
    			}
    		}
    	}
    }
    int main(){
    	scanf("%d %d %d",&n,&m,&k);
    	int s,t;
    	scanf("%d %d",&s,&t);
    	for(int i=1;i<=m;i++){
    		int u,v,w;
    		scanf("%d %d %d",&u,&v,&w);
    		g[u].push_back(node(v,w,0));
    		g[v].push_back(node(u,w,0));
    	}
    	dijkstra(s);
    	int minn=0x3f3f3f3f;
    	for(int j=0;j<=k;j++){
    		minn=min(minn,dis[t][j]);
    	}
    	printf("%d",minn);
    return 0;
    } 
    

    分层图+最短路

    分层是个好东西。

    我们把原图复制 (k) 层,然后将第一层的点和它在第一层上有连边的点在第二层对应的点连一条边权为0的有向边

    这时,你最多可以跨越 (k) 层,其实就是你最多可以用 (k) 次优惠。

    然后你就会惊奇的发现,原题直接变成一道最短路板题。

    最后枚举每一层终点的对应点,取最小的答案即可。注意存边最好使用链式前向星,会节省大量空间。

    这里用的某谷上大佬的分层实现,把它单独拎出来:

    for (int i = 1; i <= m; i++) {
        scanf("%d %d %d", &x, &y, &w);
        addedge(x, y, w);
        addedge(y, x, w);
        for (int j = 1; j <= k; j++) {
            addedge(x + (j * n), y + (j * n), w);
            addedge(y + (j * n), x + (j * n), w);
            addedge(x + ((j - 1) * n), y + (j * n), 0);
            addedge(y + ((j - 1) * n), x + (j * n), 0);
        }
    }
    for (int i = 1; i <= k; i++) 
        addedge(t + (i - 1) * n, t + i * n, 0);
    

    代码2

    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <algorithm>
    #include <queue>
    using namespace std;
    
    typedef long long LL;
    const int MAXM = 5000050;
    const int MAXN = 1000050;
    const int INF = 0x3f3f3f3f;
    struct edge {
        int to, next, val;
    } a[MAXM];
    
    struct node {
        int dist, id;
        node() {}
        node(int D, int I) {
            dist = D;
            id = I;
        }
    };
    bool operator<(node xi, node yi) { return xi.dist > yi.dist; }
    
    int n, m, k, s, t, cnt = 0, head[MAXN];
    int dist[MAXN];
    bool vis[MAXN];
    
    void Add_Edge(int x, int y, int c) {
        a[cnt].to = y;
        a[cnt].next = head[x];
        a[cnt].val = c;
        head[x] = cnt++;
    }
    
    void dijkstra(int s) {
        memset(vis, 0, sizeof(vis));
        memset(dist, INF, sizeof(dist));
        priority_queue<node> q;
        q.push(node(0, s));
        dist[s] = 0;
        while (!q.empty()) {
            node cur = q.top();
            q.pop();
            if (vis[cur.id])
                continue;
            vis[cur.id] = 1;
            for (int i = head[cur.id]; i != -1; i = a[i].next) {
                int v = a[i].to;
                if (dist[v] > a[i].val + cur.dist) {
                    dist[v] = a[i].val + cur.dist;
                    q.push(node(dist[v], v));
                }
            }
        }
    }
    int main() {
        memset(head, -1, sizeof(head));
        scanf("%d %d %d", &n, &m, &k);
        scanf("%d %d", &s, &t);
        for (int i = 1; i <= m; i++) {
            int x, y, w;
            scanf("%d %d %d", &x, &y, &w);
            Add_Edge(x, y, w);
            Add_Edge(y, x, w);
            for (int j = 1; j <= k; j++) {
                Add_Edge(x + (j * n), y + (j * n), w);
                Add_Edge(y + (j * n), x + (j * n), w);
                Add_Edge(x + ((j - 1) * n), y + (j * n), 0);
                Add_Edge(y + ((j - 1) * n), x + (j * n), 0);
            }
        }
        for (int i = 1; i <= k; i++) Add_Edge(t + (i - 1) * n, t + i * n, 0);
        dijkstra(s);
        printf("%d", dist[t + k * n]);
        return 0;
    }
    
  • 相关阅读:
    验证数字范围的小插件
    解决EJB懒加载问题
    JS获取按键的代码,Js如何屏蔽用户的按键,Js获取用户按键对应的ASII码(兼容所有浏览器)
    struts2标签之<s:select>
    c#(winform)中自定义ListItem类方便ComboBox和ListBox添加项完全解决
    辞职前须慎重考虑
    怎样把PDF文件在WinForm窗口中显示出来
    加载报表失败
    经典正则表达式 Javascript
    无法生成项目输出组“内容文件来自...
  • 原文地址:https://www.cnblogs.com/Chain-Forward-Star/p/13868313.html
Copyright © 2011-2022 走看看