题目链接 :https://acm.hdu.edu.cn/showproblem.php?pid=3001
题目大意:一个人从任意点出发访问所有城市,并且每个城市访问的次数不超过2次,问最少的花费是多少,如果到达不了,输出 -1。
详细看代码注释,已经在代码里注释的很清楚了
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define sc(x) scanf("%d",&x)
#define _sc(x) scanf("%lld",&x)
#define pf(x) printf("%d",x);
#define _pf(x) printf("%lld",x);
#define FOR(i,a,b) for(int i = a;i<=b;++i)
#define _FOR(i,a,b) for(int i = a;i<b;++i)
const ll mod = 1e9+7;
const ll maxn = 1e6+10;
const double eps = 1e-6;
const int INF = INT_MAX;
ll pow2(ll a,ll n){ ll res = 1ll;while(n){if(n&1) res = (res*a)%mod;a = (a*a)%mod;n >>= 1;}return res;}
ll gcd(ll a,ll b){ return b?gcd(b,a%b):a; }
int T;
int n,m;
int g[15][15];
int dp[15][60000];
int in[15],bit[60000][15];
void init(){
in[0] = in[1] = 1;
/*
初始化每个城市作为起点的状态 , 即 1 , 10 , 100 , 1000 (注意: 三进制下)
例如 , 三进制下的 1 十进制下是 1 , 代表第一个城市
三进制下的 10 十进制下是 3 , 代表第二个城市
三进制下的 100 十进制下是 9 , 代表第三个城市
因为每个城市都有三种状态 (0,1,2) , 城市1,2,3 三进制表示下 相当于 3的 0次幂,3的 1次幂,3 的二次幂
*/
FOR(i,2,12){
in[i] = in[i-1] * 3;
}
/*
枚举所有状态,对于每个状态 k , 查询状态下每一位的状态值(0,1,2)
*/
_FOR(k,1,in[11]){
int temp = k;
FOR(i,1,10){
bit[k][i] = temp % 3;
temp /= 3;
}
}
}
bool check(int state){
/* 检查每个状态的所有城市位置必须不等于 0 */
/*
等于 0 代表没访问过
等于 1 代表访问过一次
等于 2 代表访问郭两次
*/
FOR(i,1,n){
if(!bit[state][i]) return false;
}
return true;
}
void solve(){
memset(g,127,sizeof(g));
memset(dp,127,sizeof(dp));
FOR(i,1,m){
int u,v,w;
cin >> u >> v >> w;
// 防止重边 , 遇到重边取最小代价 w
g[u][v] = g[v][u] = min(g[u][v],w);
}
/*
dp[i][j] 目前到达第 i 个点的时候状态是 j的最小代价是dp[i][j]
*/
FOR(i,1,n){
/* 初始化 , 从每个点出发的代价为 0 */
/* 从第 i 个点 出发的状态是 in[i] , 最小代价是 0 */
dp[i][in[i]] = 0;
}
_FOR(k,0,in[n+1]){
/* 遍历所有子集 , 因为有三种状态(没走,走一次,走两次),因此对应 3的 n 次幂 */
FOR(i,1,n){
/* 枚举每个城市点作为起始点 */
/*
到达第 i 个点的状态 k 的最小代价如果大于 (1 << 30)
说明还没到达这个点,状态 k 不存在 (因为初始化为一个比 (1 << 30) 还要大的数 )
比 (1 << 30) 大即不存在到达这个点的 k 状态最小代价
*/
if(dp[i][k] >= (1 << 30)) continue;
/* 遍历相邻点 */
FOR(j,1,n){
/*
如果此状态下的第 j 位(第 j 个城市) 大于 1 证明已经走了两次不能再走了
或者 城市 i 到 城市 j 没有边到达也不能走
*/
if(bit[k][j] > 1 || g[i][j] >= (1 << 30)) continue;
/*
转移方程 dp[j][k+in[j]] = min(dp[j][k+in[j]],dp[i][k] + g[i][j]);
dp[j][k + in[j]] : 到达第 j 个点状态为 k+in[j] 的最小代价,此时的 k 是 到达 i 时的状态,所以得加上
访问 j的状态 ----> in[j] , 相当于状态 k 的第 j 位 加上 1 ,代表访问过一次 j 城市
dp[i][k] + g[i][j] : 到达第 i 个点状态为 k 的最小代价 + 从i城市到j城市的代价 (i,j)
到达 第 i 个城市状态为 k 的最小代价 + g[i][j] 表示 加上从第 i 个城市到达第 j 个城市所需的代价
*/
/*
注意:这个dp方程是更新到达 第 j 个城市状态为 k + in[j] 的最小代价 , 不是更新 dp[i][k]
*/
dp[j][k+in[j]] = min(dp[j][k+in[j]],dp[i][k] + g[i][j]);
}
}
}
int ans = (1 << 30);
_FOR(k,1,in[n+1]){
/* 遍历每一种状态 */
if(check(k)){
/* 如果该状态满足题目要求 */
FOR(i,1,n){
/* 找到从哪个城市作为结尾的最小代价 */
ans = min(ans,dp[i][k]);
}
}
}
cout << (ans == (1 << 30) ? -1 : ans) << "\n";
}
int main(void){
init();
T = 1;
while(scanf("%d %d",&n,&m) == 2){
solve();
}
return 0;
}