三进制状压 HDOJ 3001 Travelling

题目传送门

题意:从某个点出发,所有点都走过且最多走两次,问最小花费

分析:数据量这么小应该是状压题,旅行商TSP的变形。dp[st][i]表示状态st,在i点时的最小花费,用三进制状压。以后任意进制状压都会了。

#include <bits/stdc++.h>
using namespace std;

const int INF = 0x3f3f3f3f;
const int N = 11;
int dp[60000][10];
int bit[N];
int d[N][N];
int p[N];
int n, m;

void init(void)	{		//三进制0 1 2
	bit[0] = 1;
	for (int i=1; i<N; ++i)	{
		bit[i] = bit[i-1] * 3;	//bit[i] 相当于在二进制下的 1 << i
	}
}

void get_status(int st)	{
	int i = 0;
	memset (p, 0, sizeof (p));
	while (st)	{
		p[i++] = st % 3;
		st /= 3;
	}
}

int sum(void)	{
	int ret = 0;
	for (int i=0; i<n; ++i)	{
		ret += p[i] * bit[i];
	}
	return ret;
}

int main(void)	{
	init ();
	while (scanf ("%d%d", &n, &m) == 2)	{
		memset (d, INF, sizeof (d));
		for (int u, v, w, i=0; i<m; ++i)	{
			scanf ("%d%d%d", &u, &v, &w);
			u--;	v--;
			d[u][v] = d[v][u] = min (d[v][u], w);
		}
		int ans = INF;
		memset (dp, INF, sizeof (dp));
		for (int i=0; i<n; ++i)	{		//初始化,三进制下10000的状态,现在在第i个点时花费0
			dp[bit[i]][i] = 0;
		}
		for (int i=0; i<bit[n]; ++i)	{
			bool flag = true;
			get_status (i);
			for (int j=0; j<n; ++j)	{
				if (p[j] == 0)	flag = false;
				if (dp[i][j] == INF)	continue;
				for (int k=0; k<n; ++k)	{
					if (p[k] == 2 || d[j][k] == INF || k == j)	continue;
					p[k]++;
					int v = sum ();
					dp[v][k] = min (dp[v][k], dp[i][j] + d[j][k]);
					p[k]--;
				}
			}
			if (flag)	{		//每个点至少都走过一次的状态
				for (int j=0; j<n; ++j)	{
					ans = min (ans, dp[i][j]);
				}
			}
		}
		if (ans == INF)	ans = -1;
		printf ("%d
", ans);
	}

	return 0;
}

  

编译人生,运行世界!
原文地址:https://www.cnblogs.com/Running-Time/p/4986282.html