题目
题解
我们可以在输入的时候对坚果进行预处理,处理出每个坚果之间和与起点之间的距离。
然后考虑进行状态压缩设
dp[S][u]
d
p
[
S
]
[
u
]
表示当前吃到的坚果的集合为
S
S
,当前吃到的坚果为号
转移的话就非常容易了:
dp[S]|(1<<(j−1))[v]=min(dp[S|(1<<(j−1))][v],dp[S][u]+G[u][v])
d
p
[
S
]
|
(
1
<<
(
j
−
1
)
)
[
v
]
=
m
i
n
(
d
p
[
S
|
(
1
<<
(
j
−
1
)
)
]
[
v
]
,
d
p
[
S
]
[
u
]
+
G
[
u
]
[
v
]
)
.
最后输出的时候遍历最后的状态然后加上到起点的距离取
min
m
i
n
就可以
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
using namespace std;
struct node
{
int x,y;
}node[20];
int n,m,cnt;
int G[20][20],dp[1<<17][17];
char s[30];
void Built()
{
for(int i=0;i<=cnt;i++)
for(int j=0;j<=cnt;j++)
G[i][j]=G[j][i]=abs(node[i].x-node[j].x)+abs(node[i].y-node[j].y);
}
void solve()
{
int sum=(1<<cnt)-1;
for(int i=1;i<=cnt;i++)
dp[(1<<(i-1))][i]=G[i][0];
for(int S=0;S<sum;S++)
{
for(int u=1;u<=cnt;u++)
{
if(!((1<<(u-1))&S)) continue;
for(int v=1;v<=cnt;v++)
{
if(u==v) continue ;
if(((1<<(v-1))&S)) continue ;
dp[S|(1<<(v-1))][v]=min(dp[S|(1<<(v-1))][v],dp[S][u]+G[u][v]);
}
}
}
int ans=1e8;
for(int i=1;i<=cnt;i++)
ans=min(ans,dp[sum][i]+G[i][0]);
printf("%d\n",ans);
}
inline void clear()
{
memset(node,0,sizeof(node));
memset(dp,0x7f7f7f7f,sizeof(dp));
memset(G,0,sizeof(G));
cnt=0;
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
clear();
for(int i=1;i<=n;i++)
{
scanf("%s",s);
for(int j=0;j<m;j++)
{
if(s[j]=='L')
node[0].x=i,node[0].y=j+1;
else
if(s[j]=='#')
node[++cnt].x=i,node[cnt].y=j+1;
}
}
Built();
solve();
}
return 0;
}