郊区春游(状压dp)

郊区春游

题意:
给出 n n n个地方, m m m条路,每条路都有一个权值 v v v R R R个想去的地方,问怎么安排行程使花费最少。

思路:
首先我们用 F l o y d ( O ( n 3 ) ) Floyd(O(n^3)) Floyd(O(n3))求每两个地方的最小花费。
再因为 R R R的取值范围是 [ 2 , 15 ] [2,15] [215],我们可以用二进制位 ( 0 o r 1 ) (0or1) (0or1)来表示某个位置是否已经参观过,例如 6 = ( 0110 ) 2 6=(0110)_2 6=(0110)2,就可以表示第1个和第2个地方我们已经参观过了。
之后我们用 d p [ i ] [ j ] dp[i][j] dp[i][j]来表示 i , j i,j i,j状态下的最小花费。其中 i i i是上面说到的二进制表示, j j j表示此刻在第 j j j个位置(注意dp初始化)。
现在就是状态的转移:
d p [ i + ( 1 < < k ) ] [ k ] = m i n ( d p [ i + 1 < < k ] [ k ] , d p [ i ] [ j ] + F [ g [ j ] [ g [ k ] ] dp[i+(1<<k)][k] = min(dp[i+1<<k][k], dp[i][j]+F[g[j][g[k]] dp[i+(1<<k)][k]=min(dp[i+1<<k][k],dp[i][j]+F[g[j][g[k]]
方程的含义就是是否要通过状态 d p [ i ] [ j ] dp[i][j] dp[i][j]达到 d p [ i + ( 1 < < k ) ] [ k ] dp[i+(1<<k)][k] dp[i+(1<<k)][k]的状态(选择花费较小的)。其中的 i + ( 1 < < k ) i+(1<<k) i+(1<<k),i并没有经过第k个地方,当 i + ( 1 < < k ) i+(1<<k) i+(1<<k)后第 k k k个位置上就将变成 1 1 1

我们将 i i i在区间 [ 1 , 2 r − 1 ] [1,2^r-1] [1,2r1]枚举,枚举每种选择状态。 j j j k k k都在在区间 [ 0 , r ] [0, r] [0,r]上枚举。
最后取 m a x ( d p [ ( 1 < < r ) − 1 ] [ i ] ) ( i ∈ [ 0 , r ] ) max(dp[(1<<r)-1][i])(i\in[0, r]) max(dp[(1<<r)1][i])(i[0,r])

C o d e Code Code

#include<bits/stdc++.h>
using namespace std;
const int inf = 1<<23;
const int N = 20;
const int M = 210;
int g[N], F[M][M], dp[1 << 15][20];
int main() {
    
    
    int n, m, r, a, b, c;
    cin >> n >> m >> r;
    for(int i=0; i<r; i++) cin >> g[i];
    //初始化F数组
    for(int i=1; i<=n; i++) {
    
    
        for(int j=1; j<=n; j++) {
    
    
            if(i != j) F[i][j] = inf;
        }
    }
    //读入每条路径
    for(int i=0; i<m; i++) {
    
    
        cin >> a >> b >> c;
        F[a][b] = min(F[a][b], c);
        F[b][a] = F[a][b];
    }
    //floyd求两点之间最小花费
    for(int i=1; i<=n; i++) {
    
    
        for(int j=1; j<=n; j++) {
    
    
            for(int k=1; k<=n; k++) {
    
    
                F[j][k] = min(F[j][k], F[j][i] + F[i][k]);
            }
        }
    }
    //初始化dp数组
    memset(dp, 0x7f, sizeof dp);
    for(int i=0; i<r; i++) {
    
    
        dp[1 << i][i] = 0;
    }
    //枚举i,j,k。状态转移
    for(int i=1; i<(1 << r)-1; i++) {
    
    
        for(int j=0; j<r; j++) {
    
    
            if(i & (1 << j)) {
    
    
                for(int k=0; k<r; k++) {
    
    
                    if(!(i & (1 << k))) 
                    dp[i + (1 << k)][k] = min(dp[i + (1 << k)][k], dp[i][j] + F[g[j]][g[k]]);
                }
            }
        }
    }
    //取最大值,得到答案。
    int ans = inf;
    for(int i=0; i<r; i++) {
    
    
        ans = min(ans, dp[(1 << r)-1][i]);
    }
    cout << ans;
    return 0;
}


猜你喜欢

转载自blog.csdn.net/weixin_45363113/article/details/108063719