HDU3001 状态压缩DP+三进制

题意描述

原题链接

n个城市,m条路,每个城市最多经过2次,遍历所有城市最小的费用是多少

如果没有则输出-1

输入是多组数据

\(n \le 10\)

算法解析

首先我们观察数据范围,不难得出应该是状态压缩DP。

于是第一眼就该是

f[i]表示当前已经访问的城市节点们

问题是当前城市访问次数需要统计

于是放弃二进制,来存储这个城市是否访问过

而是,采用三进制,表示这个城市的访问次数,0为未访问

然后需要知道当前位于的城市

所以f[i][j]表示状态为i,当前所在城市为j

那么我们就可以通过当前节点,拓展到其他节点了。

然后判断依据就是,下一个节点是否访问次数<=2

int now=i+bit[k];//访问当前点1次
f[now][k]=min(f[now][k],f[i][j]+dis[j][k]);

如果没有看懂,可以在下面评论要求视频讲解

代码解析

/*
f[i]表示当前访问的城市节点

问题是当前城市访问次数需要统计

于是放弃二进制,来存储这个城市是否访问过

而是,采用三进制,表示这个城市的访问次数,0为未访问

然后需要知道当前位于的城市

所以f[i][j]表示状态为i,当前所在城市为j
*/
#include <cstdio>
#include <cstring>
#include <algorithm>s
using namespace std;
const int N=211000;//3^10=59049
//这里一定要开大N,因为第N位,我们可能要算2次,再加上其他位,所以开3倍最为保险
//否则就会RE,我就是在这里挂掉的
int f[N][13],bit[13],dis[21][21],n,m;
#define state(a,b) ((a)/(bit[(b)])%3)//a状态b位置的访问次数 
inline void init()
{
	bit[0]=1;
	for(int i=1; i<=11; i++)//规定三进制的每一位
		bit[i]=bit[i-1]*3;
	while(scanf("%d%d",&n,&m)!=EOF)
	{
		memset(f,0x3f,sizeof(f));
		memset(dis,0x3f,sizeof(dis));
		for(int i=1; i<=m; i++)
		{
			int a,b,c;
			scanf("%d%d%d",&a,&b,&c);
			dis[a][b]=dis[b][a]=min(dis[a][b],c);
		}
		int ans=1e9;
		for(int i=1; i<=n; i++)
			f[bit[i]][i]=0;//把每个点作为出发点,费用均为0
		for(int i=1; i<bit[n+1]; i++)//这里是访问所有状态
		{
			int ok=1;//假设所有的点都已经被访问过1次了
			for(int j=1; j<=n; j++)
			{
				if (state(i,j)==0)//所有点并没有被访问过
				{
					ok=0;
					continue;
				}
				//接下来开始转移节点
				for(int k=1; k<=n; k++)
				{
					if (state(i,k)==2)//不可以再次访问
						continue;
					int now=i+bit[k];//访问当前点1次
					f[now][k]=min(f[now][k],f[i][j]+dis[j][k]);
					//从点j转移到点k,访问一次,计算费用
				}
			}
			if (ok)//满足全部访问的基本条件了
			{
				for(int j=1; j<=n; j++)
					ans=min(ans,f[i][j]);
			}
		}
		if (ans==1e9)
			ans=-1;
		printf("%d\n",ans);
	}
}
signed main()
{
	init();
	return 0;
}

/*Example
2 1
1 2 100
3 2
1 2 40
2 3 50
3 3
1 2 3
1 3 4
2 3 10
*/
posted @ 2021-03-29 23:43  秦淮岸灯火阑珊  阅读(181)  评论(0编辑  收藏  举报