cf793 D. Presents in Bankopolis(区间dp)

题意:

给定一个有向图,求恰经过k个点的最短路径的长度。

每个点最多走一次。且走一条新边 x->y 时,不能有一个之前走过的点的编号 u 在 x,y 之间(即 \(min(x,y)<u<max(x,y)\)

\(1\le n,k \le 100, 0\le m \le 2000\)

思路:

这题跟图没什么关系。把图看成一条 \([1,n]\) 整点线段,画图模拟一下走的过程。

考虑反向过程,把所有边都反向。\(f(k,l,r,0/1)\) 表示 \(l\to r\) 作为第 \(k-1\) 条边(走了 \(k\) 个点),方向是x轴负向/正向,的最短路长度

用题目给的边初始化 \(f(2,\cdots)\),即走了一条边,两个点

注意特判,k=1时只能到一个点,输出1。

const int N = 105;
int n, m, K, w[N][N], f[N][N][N][2], ans = INF;

signed main()
{
    memset(w, INF, sizeof w);
    memset(f, INF, sizeof f);

    cin >> n >> K >> m;
    while(m--)
    {
        int a, b, c; cin >> b >> a >> c; //反向
        w[a][b] = min(w[a][b], c); //边权,小心重边
        if(a < b) f[2][a][b][1] = w[a][b];
        else f[2][b][a][0] = w[a][b];
    }

    if(K == 1) return cout << 0, 0;

    for(int k = 3; k <= K; k++)
        for(int l = 1; l <= n - 1; l++)
        for(int r = l + 1; r <= n; r++)
            for(int x = l + 1; x < r; x++)
                f[k][l][r][1] = min({f[k][l][r][1],
                    f[k-1][l][x][0] + w[l][r], f[k-1][l][x][1] + w[x][r]}),
                f[k][l][r][0] = min({f[k][l][r][0],
                    f[k-1][x][r][0] + w[x][l], f[k-1][x][r][1] + w[r][l]});

    for(int l = 1; l <= n - 1; l++)
        for(int r = l + 1; r <= n; r++)
            ans = min({ans, f[K][l][r][0], f[K][l][r][1]});
    if(ans == INF) ans = -1;
    cout << ans;
}

posted @ 2022-03-01 23:44  Bellala  阅读(46)  评论(0)    收藏  举报