[OpenJudge 4124 海贼王之伟大航路] 二进制状态压缩+记忆化DFS/ 状压DP
题目链接:[OpenJ_Bailian - 4124 海贼王之伟大航路]
题意:N个顶点之间构成无向图。求遍历所有顶点一次的情况下,从1~N的最短路长度。(2 < N ≤ 16)
分析:
思路一 记忆化DFS:
直接暴力DFS肯定不行,时间复杂度是(N - 2)! 。然后就想利用状态剪枝。 用dp[u][state] 表示从1~u,中间已经经过的每个点对应state一位二进制位 的最小的距离。 如果遍历到u 的距离小于dp[u][state],就可以进行剪枝了。
思路二 状压DP:
dp[u][state] = min(dp[u][state], dp[i][state ^ (1<<(i - 1))] + G[u][i]);
/**
* 思路一:记忆化DFS
*/
#include <ctime>
#include <cmath>
#include <queue>
#include <vector>
#include <cstdio>
#include <string>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <algorithm>
using namespace std;
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#define FIN freopen("input.txt","r",stdin)
#define FOUT freopen("output.txt","w",stdout)
#define fst first
#define snd second
//typedef __int64 LL;
//typedef long long LL;
typedef unsigned int uint;
typedef pair<int, int> PII;
const int INF = 0x3f3f3f3f;
const int MAXN = 16 + 5;
int N;
int G[MAXN][MAXN];
int dp[MAXN][ (1 << 16) + 5];
int cnt, finish;
void dfs (int u, int d, int state) {
// cnt++;
if (u == N) {
if (u == N && state == finish) dp[N][state] = min (dp[N][state], d);
return;
}
if (d >= dp[u][state] || d >= dp[N][finish]) return;
dp[u][state] = d;
int v, f;
for (v = 2, f = 2; v < N; v++, f <<= 1) {
if (state & f) continue;
dfs (v, d + G[u][v], state | f);
}
if (state == (finish ^ f)) dfs (v, d + G[u][v], state | f);
return;
}
int main() {
#ifndef ONLINE_JUDGE
FIN;
#endif // ONLINE_JUDGE
// clock_t start, end;
// start = clock();
while (~scanf ("%d", &N) ) {
for (int i = 1; i <= N; i++) {
for (int j = 1; j <= N; j++) {
scanf ("%d", &G[i][j]);
}
}
finish = (1 << N) - 1;
memset (dp, INF, sizeof (dp) );
cnt = 0;
dfs (1, 0, 1);
// cout << cnt << "\t";
printf ("%d\n", dp[N][finish]);
}
// end = clock();
// printf ("time=%f\n", (double) (end - start) / CLK_TCK);
return 0;
}
/**
* 思路二:状压DP
*/
#include <ctime>
#include <cmath>
#include <queue>
#include <vector>
#include <cstdio>
#include <string>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <algorithm>
using namespace std;
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#define FIN freopen("input.txt","r",stdin)
#define FOUT freopen("output.txt","w",stdout)
#define fst first
#define snd second
//typedef __int64 LL;
//typedef long long LL;
typedef unsigned int uint;
typedef pair<int, int> PII;
const int INF = 0x3f3f3f3f;
const int MAXN = 16 + 5;
int N;
int G[MAXN][MAXN];
int dp[MAXN][ (1 << 16) + 5];
int finish;
int main() {
#ifndef ONLINE_JUDGE
FIN;
#endif // ONLINE_JUDGE
// clock_t start, end;
// start = clock();
while (~scanf ("%d", &N) ) {
for (int i = 1; i <= N; i++) {
for (int j = 1; j <= N; j++) {
scanf ("%d", &G[i][j]);
}
}
finish = (1 << N) - 1;
memset (dp, INF, sizeof (dp) );
dp[1][1] = 0;
for (int k = 1; k <= finish; k++) {
for (int i = 1, fi = 1; i <= N; i++, fi <<= 1) {
if (! (k & fi) ) continue;
for (int j = 1, fj = 1; j <= N; j++, fj <<= 1) {
if (i == j || ! (k & fj) ) continue;
dp[i][k] = min (dp[i][k], dp[j][k ^ fi] + G[j][i]);
}
}
}
printf ("%d\n", dp[N][finish]);
}
// end = clock();
// printf ("time=%f\n", (double) (end - start) / CLK_TCK);
return 0;
}