poj 3311 送披萨 状压dp

本文详细阐述了如何解决TSP问题,包括预处理最短路径、枚举全排列找到最小路径以及使用动态规划的方法。通过实例代码展示了算法的具体实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题木:点击打开链接

题意:

位于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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值