题木:点击打开链接
题意:
位于0点小伙计给n个点送披萨,每个点可以经过多次,问送完n个点回到0的最短路程。类似于TSP问题,不过TSP每个点只可以经过一次。
分析:
对于这题,因为一个点可以经过多次,所以可以预处理两个点的最短距离,这个距离可以经过多次。处理完之后,就好办了,可以枚举n个点的全排列,找出最小的就好,比较简单。对于这题,当然也可以用dp,状态的表示 f[st][i],表示现在在i点,状态是st的最短路程。那么状态转移显而易见:f[st][i]=min( f[{st}-i][j]+dis[j][i] ).
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int INF=0x3f3f3f3f;
int s[12][12],dis[12][12],f[1<<12][12];
int main()
{
int n;
//freopen("f.txt","r",stdin);
while(~scanf("%d",&n)&&n){
for(int i=0;i<=n;i++){
for(int j=0;j<=n;j++){
scanf("%d",&dis[i][j]);
//dis[i][j]=s[i][j];
}
}
for(int j=0;j<=n;j++){
for(int i=0;i<=n;i++){
for(int k=0;k<=n;k++){
if(dis[i][j]>dis[i][k]+dis[k][j])
dis[i][j]=dis[i][k]+dis[k][j];
}
}
}
for(int st=0;st<(1<<n);st++){
for(int i=1;i<=n;i++){
if(st&(1<<(i-1))){
if(st==(1<<(i-1)))
f[st][i]=dis[0][i]; //边界处理,st状态表示现在直走到一个点
else{
f[st][i]=INF;
for(int j=1;j<=n;j++){
if(st&(1<<(j-1))&&j!=i)
f[st][i]=min(f[st^(1<<(i-1))][j]+dis[j][i],f[st][i]);
}
}
}
}
}
int ans=f[(1<<n)-1][1]+dis[1][0];
for(int i=2;i<=n;i++)
ans=min(ans,f[(1<<n)-1][i]+dis[i][0]);
printf("%d\n",ans);
}
return 0;
}
在TSP问题中,是给出n个点,每个点都遍历一遍,最后回到原点的最短路。在这题中,也可以看成是n+1个点,每个点都遍历一遍(可以重复),求回到原点(即0点)的最短距离。
所以可以在换一种状态表示 f[st][i],表示当前在i点,状态是st。那么答案就是遍历完n+1个点,停在0的dp值
状态转移方程是:
if(f[st|(1<<j)][j]==-1)f[st|(1<<j)][j]=f[st][i]+dis[i][j]; //j是下一个要去的点,并入st状态表示j状态已经去过了,并且加上dis(i,j)
else
f[st|(1<<j)][j]=min(f[st|(1<<j)][j],f[st][i]+dis[i][j]); //寻找最小的从i去j点的最小值
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int INF=0x3f3f3f3f;
int s[12][12],dis[12][12],f[1<<12][12];
int main()
{
int n;
//freopen("f.txt","r",stdin);
while(~scanf("%d",&n)&&n){
for(int i=0;i<=n;i++){
for(int j=0;j<=n;j++){
scanf("%d",&dis[i][j]);
//dis[i][j]=s[i][j];
}
}
for(int j=0;j<=n;j++){
for(int i=0;i<=n;i++){
for(int k=0;k<=n;k++){
if(dis[i][j]>dis[i][k]+dis[k][j])
dis[i][j]=dis[i][k]+dis[k][j];
}
}
}
memset(f,-1,sizeof(f));
f[1][0]=0;
for(int st=0;st<(1<<(n+1));st++){
st=st|1;
for(int i=0;i<=n;i++){
if(f[st][i]!=-1){
for(int j=0;j<=n;j++){
if(j==i)continue;
if(f[st|(1<<j)][j]==-1)f[st|(1<<j)][j]=f[st][i]+dis[i][j];
else
f[st|(1<<j)][j]=min(f[st|(1<<j)][j],f[st][i]+dis[i][j]);
}
}
}
}
printf("%d\n",f[(1<<(n+1))-1][0]);
}
return 0;
}