【状压DP】售货员的难题

P1171

题目描述

某乡有n个村庄(1

输入

村庄数n和各村之间的路程(均是整数)。

输出

最短的路程。

输入

3
0 2 1
1 0 2
2 1 0

输出

3

题解

首先思考暴力解法

此题如果暴搜状态数过大,考虑状态压缩

可以发现此题状态转移非常简单,考虑DP

状态设计成两维,dp[ i ][ j ]表示当售货员到达村庄 i 时,所有村庄访问情况为 j 的最小距离(j 为二进制状压)

状态转移方程

dp[i][(1 << (i - 1)) | m] = min(dp[i][(1 << (i - 1) | m)], dp[j][m] + a[j][i]);

关于状压DP可参考本人博客 链接

代码

// luogu-judger-enable-o2
#include
using namespace std;

int n;
int a[27][27];
int dp[27][(1 << 20) + 7];//走到村庄i时所有村庄的访问状态为j 
int ans = 0x3f3f3f3f;

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 << 1) + (x << 3) + (ch ^ 48); ch = getchar();}
    return x * f;
}


int main() {
    n = read();
    for (int i = 1; i <= n; i++) {
	for (int j = 1; j <= n; j++) {
	    a[i][j] = read();
	}
    }
    memset(dp, 0x3f3f3f3f, sizeof dp);
    dp[1][1] = 0;
    for (int m = 0; m < (1 << n); m++) {//当前状态 
	for (int i = 1; i <= n; i++) {//下一个村庄 
	    if ((1 << (i - 1)) & m) continue;
            for (int j = 1; j <= n; j++) {//当前村庄 
		if ((1 << (j - 1)) & m) {
	            dp[i][(1 << (i - 1)) | m] = min(dp[i][(1 << (i - 1) | m)], dp[j][m] + a[j][i]);
		}
            }
	}
    }
    for (int i = 2; i <= n; i++) {
	ans = min(ans, dp[i][(1 << n) - 1] + a[i][1]);
    }
    printf("%d", ans);
}


 

你可能感兴趣的:(DP)