吃奶酪——状压DP

洛谷p1433

题目大意

给出n个点的坐标,求从原点出发遍历这些点的最短路径。n<=15。

题解

这道题确实可以用DFS做,但我还是想讲一讲状压DP。状压DP就是状态压缩动态规划。我们把每一种状态看成(仅仅是看成)一个二进制数,第i位上是1就代表i这个点已经走过了,是0就代表i这个点还没走过。我们用f[i][j] 表示这一步走到i点,所处的状态是j时,所走过的最短距离。特殊的,我们可以把原点当做第0个点来处理。这样就有2^(n+1)种状态。转移方程是这样的:

f[i][j]=min(f[i][j],f[k][j-(1<<i)]+dist(a[i],a[k]));

就是上一步在k点,这一步走到了i点。当然,这个方程的前提是,在j这个状态里,i点的那一位是1;在j-(1<< i)这个状态里,k点那一位也是1。题目说从原点开始走,也可以看成是以原点为终点,所以最终答案是f[0][(1<<(n+1))-1]。复杂度是O(n^2*2^n)。
代码如下:

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
#define INF 1000000000
struct node
{
    double x,y;
}a[20];
int n;
double f[20][100000];
double dist(node a,node b)
{
    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
int main()
{
    cin>>n;
    for (int i=1;i<=n;i++) cin>>a[i].x>>a[i].y;
    for (int i=0;i<=n;i++)
    for (int j=0;j<(1<<(n+1));j++) f[i][j]=INF;
    for (int i=0;i<=n;i++) f[i][1<<i]=0;
    for (int j=0;j<(1<<(n+1));j++)
    {
        for (int i=0;i<=n;i++)
        {
            if ((j&(1<<i))==0) continue;
            for (int k=0;k<=n;k++)
            {
                if (((1<<k)&j)!=0&&i!=k)//尤其要注意(1<<k)&j两边的括号不能去掉 
                {
                    f[i][j]=min(f[i][j],f[k][j-(1<<i)]+dist(a[i],a[k]));
                }
            }

        }
    }
    printf("%.2lf",f[0][(1<<(n+1))-1]);
}

总结

数组要开得够大,不然会RE,起码要开2^16(我开的是100000),因为算上原点,一共16个点有2^16种状态。关于位运算,像&,<<这种,一定要记住,能加括号的就要加,因为它不一定是从左到右运算的,有优先级的,不知道优先级就只好乖乖地都加括号。我就是没加括号,调试了很久,又看不出来哪里错了。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值