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
*/