poj 3311 送披萨 状压dp

题木:点击打开链接

题意:

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

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
题目描述 给出一个$n\times m$的矩阵,每个位置上有一个非负整数,代表这个位置的海拔高度。一开始时,有一个人站在其中一个位置上。这个人可以向上、下、左、右四个方向移动,但是只能移动到海拔高度比当前位置低或者相等的位置上。一次移动只能移动一个单位长度。定义一个位置为“山顶”,当且仅当从这个位置开始移动,可以一直走到海拔高度比它低的位置上。请问,这个矩阵中最多有多少个“山顶”? 输入格式 第一行两个整数,分别表示$n$和$m$。 接下来$n$行,每行$m$个整数,表示整个矩阵。 输出格式 输出一个整数,表示最多有多少个“山顶”。 样例输入 4 4 3 2 1 4 2 3 4 3 5 6 7 8 4 5 6 7 样例输出 5 算法1 (递归dp) $O(nm)$ 对于这道题,我们可以使用递归DP来解决,用$f(i,j)$表示以$(i,j)$为起点的路径最大长度,那么最后的答案就是所有$f(i,j)$中的最大值。 状态转移方程如下: $$ f(i,j)=\max f(x,y)+1(x,y)是(i,j)的下一个满足条件的位置 $$ 注意:这里的状态转移方程中的$x,y$是在枚举四个方向时得到的下一个位置,即: - 向上:$(i-1,j)$ - 向下:$(i+1,j)$ - 向左:$(i,j-1)$ - 向右:$(i,j+1)$ 实现过程中需要注意以下几点: - 每个点都需要搜一遍,因此需要用双重for循环来枚举每个起点; - 对于已经搜索过的点,需要用一个数组$vis$来记录,防止重复搜索; - 在进行状态转移时,需要判断移动后的点是否满足条件。 时间复杂度 状态数为$O(nm)$,每个状态转移的时间复杂度为$O(1)$,因此总时间复杂度为$O(nm)$。 参考文献 C++ 代码 算法2 (动态规划) $O(nm)$ 动态规划的思路与递归DP类似,只不过转移方程和实现方式有所不同。 状态转移方程如下: $$ f(i,j)=\max f(x,y)+1(x,y)是(i,j)的下一个满足条件的位置 $$ 注意:这里的状态转移方程中的$x,y$是在枚举四个方向时得到的下一个位置,即: - 向上:$(i-1,j)$ - 向下:$(i+1,j)$ - 向左:$(i,j-1)$ - 向右:$(i,j+1)$ 实现过程中需要注意以下几点: - 每个点都需要搜一遍,因此需要用双重for循环来枚举每个起点; - 对于已经搜索过的点,需要用一个数组$vis$来记录,防止重复搜索; - 在进行状态转移时,需要判断移动后的点是否满足条件。 时间复杂度 状态数为$O(nm)$,每个状态转移的时间复杂度为$O(1)$,因此总时间复杂度为$O(nm)$。 参考文献 C++ 代码

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值