做这题前,先做poj3311,2进制的状态压缩dp,除了状态压缩不一样,剩下的都异曲同工。
http://blog.csdn.net/u013508213/article/details/46367815
题意:
可以任选一个城市出发,每个城市可以经过两次,要求遍历所有城市之后回到这个任选的城市,求最小的花费。
解析:
因为每个城市可以经过两次,所以对于每一个城市,可以用一个三进制数来表示其状态,0, 1, 2表示城市没有被访问过,被访问过1次,被访问过2次。
dp[ j ] [ state ] 表示 从 j 这个点出发,在state状态下,最小的花费。
状态转移方程和2进制的一样:
dp [ j ] [ state ] = min ( dp [ j ] [ state ] , dp[ k ] [ state - three[ j ] ] + g[ k ] [ j ] )
意义也是,在状态 state 下,回到 j 点没有被此次访问的状态,枚举前一点 k ,比较 经过 k 到达 j 的最短花费。
代码:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <stack>
#include <vector>
#include <queue>
#include <map>
#include <climits>
#include <cassert>
#define LL long long
#define lson lo, mi, rt << 1
#define rson mi + 1, hi, rt << 1 | 1
using namespace std;
const int inf = 0x3f3f3f3f;
int dp[12][59050];
int g[12][12];
int three[12];
int num[59050][12];
int n, m;
void threeTable()
{
three[1] = 1;
for (int i = 2; i <= 12; i++)
{
three[i] = three[i - 1] * 3;
}
for (int state = 0; state < three[11]; state++)
{
int t = state;
for (int j = 1; j <= 10; j++)
{
num[state][j] = t % 3;
t /= 3;
}
}
}
int main()
{
#ifdef LOCAL
freopen("in.txt", "r", stdin);
#endif // LOCAL
threeTable();
while (~scanf("%d%d", &n, &m))
{
memset(dp, inf, sizeof(dp));
memset(g, inf, sizeof(g));
int ans = inf;
while (m--)
{
int fr, to, cost;
scanf("%d%d%d", &fr, &to, &cost);
if (cost < g[fr][to])
g[fr][to] = g[to][fr] = cost;
}
for (int i = 1; i <= n; i++)
dp[i][three[i]] = 0;//起点
for (int state = 0; state < three[n + 1]; state++)
{
bool flag = true;
for (int j = 1; j <= n; j++)
{
if (num[state][j] == 0)//每个点都要被遍历过
flag = false;
for (int k = 1; k <= n; k++)
{
// cout << num[state][j] << endl;
if (num[state][j] != 0)
{
dp[j][state] = min(dp[j][state], dp[k][state - three[j]] + g[k][j]);
}
}
}
// cout << flag << endl;
if (flag)
{
for (int i = 1; i <= n; i++)
{
ans = min(ans, dp[i][state]);
}
}
}
if (ans == inf)
ans = -1;
printf("%d\n", ans);
}
return 0;
}