no crossing (区间dp 逆向思维)

42 篇文章 0 订阅
24 篇文章 0 订阅

no crossing - 题目 - Daimayuan Online Judge

题意:

给出一个有向图,找一条恰好经过 k 个点的最短路径,要求每次选的边不能跃过之前已经经过的节点。即对于路径中的边 x→y ,不存在以前经过的点 tt 使得三者的编号满足 min(x,y)≤t≤max(x,y)

输入格式

第一行三个数字 n,k,m

接下来m行 , 每行 3 个整数 ai,bi,ci表示存在一条从 ai→bi , 长度为ci 的有向边。

解题思路:

题目的意思即为,在路径上,找这样的走法:

具体意思即是后来的节点的路径不能覆盖走过的点。正向的想的话,就是左右缩进的过程;逆向从小区间到大区间更新求解;

由于这样走,对之前的状态和之后的状态都不关心,符合dp的特性。那么我们可以用区间dp来解;

有向图的方向问题:0代表向右,1向左;

 

 

参考代码: 

/*Keep on going Never give up*/
#include <bits/stdc++.h>
using namespace std;
#define MAX 0x7fffffff
#define pi acos(-1.0)
#define int long long
const int mod = 1e9+7;
const int N = 110;
int f[N][N][N][2];
const int inf = 1e9;
typedef pair<int, int> PII;
vector<PII> adj[N];
signed main() {
    int n, m, k;
    cin >> n >> k >> m;
    while (m--) {
        int a, b, c;
        cin >> a >> b >> c;
        adj[a].push_back({b, c});
    }
    for (int k = 1; k <= n; k++) {//已经走了K个节点;
        for (int l = 0; l <= n + 1; l++) {
            for (int r = l + 1; r <= n + 1; r++) {//对于每个区间:
                f[k][l][r][0] = inf;//分入左端点的区间和入右端点的区间;
                for (auto u : adj[l]) {//进左端点的:可到达左端点的点枚举:如果左端点可到达的节点在左区间到右区间之间,那么就是可行的。
                    if (u.first > l && u.first < r) {
                        f[k][l][r][0] = min(f[k][l][r][0], f[k - 1][l][u.first][1] + u.second);
                        //调个头;
                        f[k][l][r][0] = min(f[k][l][r][0], f[k - 1][u.first][r][0] + u.second);
                        //[u.first][r]左端点出来;
                    }
                }
                f[k][l][r][1] = inf;
                for (auto u : adj[r]) {//从右端点进:
                    if (u.first > l && u.first < r) {
                        f[k][l][r][1] = min(f[k][l][r][1], f[k - 1][l][u.first][1] + u.second); //同上。
                        f[k][l][r][1] = min(f[k][l][r][1], f[k - 1][u.first][r][0] + u.second);
                    }
                }
            }
        }
    }
    int res = inf;
    for (int i = 1; i <= n; i++) {//编号1到n,按合法的路径统计:
        res = min(res, f[k - 1][0][i][1]);//反向就是0向l节点;
        res = min(res, f[k - 1][i][n + 1][0]);
    }
    if (res == inf)
        cout << -1 << endl;
    else
        cout << res << endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值