Travelling HDU-3001

题目链接 :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;
} 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值