Leedcode P1433 吃奶酪

题目描述

房间里放着 nn 块奶酪。一只小老鼠要把它们都吃掉,问至少要跑多少距离?老鼠一开始在 (0,0)(0,0) 点处。

输入格式

第一行有一个整数,表示奶酪的数量 nn。

第 22 到第 (n + 1)(n+1) 行,每行两个实数,第 (i + 1)(i+1) 行的实数分别表示第 ii 块奶酪的横纵坐标 x_i, y_ixi,yi

输出格式

输出一行一个实数,表示要跑的最少距离,保留 22 位小数。

输入输出样例

输入 #1
4
1 1
1 -1
-1 1
-1 -1
输出 #1
7.41

说明/提示

数据规模与约定

对于全部的测试点,保证 1\leq n\leq 151n15,|x_i|, |y_i| \leq 200xi,yi200,小数点后最多有 33 位数字。

提示

对于两个点 (x_1,y_1)(x1,y1),(x_2, y_2)(x2,y2),两点之间的距离公式为 \sqrt{(x_1-x_2)^2+(y_1-y_2)^2}(x1x2)2+(y1y2)2

#include<iostream>
#include<cstring>
#include<cmath>
#include<iomanip>
#define min(a,b) (((a)<(b))?(a):(b))
using namespace std; 
double a[16][16],F[16][34000];
double x[16],y[16];
int n;
double dis(int v,int w){
    return sqrt((x[v]-x[w])*(x[v]-x[w])+(y[v]-y[w])*(y[v]-y[w]));
}
int main(){
    cin>>n;
    for(int i=1;i<=n;i++)
    cin>>x[i]>>y[i];
    double ans;
    memset(F,127,sizeof(F));
    ans=F[0][0];
    x[0]=0;
    y[0]=0;
    for(int i=0;i<=n;i++)
    for(int j=0;j<=n;j++)
    a[i][j]=dis(i,j);//记录所有距离,包括原点 
    for(int i=1;i<=n;i++)
    F[i][1<<(i-1)]=a[0][i];//所有到i点且只经过i点的F[][]均为i点到原点距离 
    for(int k=1;k<(1<<n);k++)//最后的结果是F[i][k],从j出发走到i点 
    {
        for(int i=1;i<=n;i++)
        {
            if(k&(1<<(i-1)==0))//F[][]还没有走过i 
            continue;
            for(int j=1;j<=n;j++){
                if(i==j)//不能自己走到自己 
                continue;
                if(k&(1<<(j-1)==0))//F[][]还没走过j 
                continue;
                F[i][k]=min(F[i][k],F[j][k-(1<<(i-1))]+a[i][j]);
            }
        }
     } 
         for(int i=1;i<=n;i++){
            ans=min(ans,F[i][(1<<n)-1]);
        }
        cout<<fixed<<setprecision(2)<<ans<<endl;
    return 0;
}

状压dp

这题dfs肯定超时。状压dp的时间复杂度为n*2^n。

状压就是用一个整数对应的二进制数来表示状态

那么首先要了解位运算

  1. ’&’符号,x&y,会将两个十进制数在二进制下进行与运算(都1为1,其余为0) 然后返回其十进制下的值。例如3(11)&2(10)=2(10)。
  2. ’|’符号,x|y,会将两个十进制数在二进制下进行或运算(都0为0,其余为1) 然后返回其十进制下的值。例如3(11)|2(10)=3(11)。
  3. ’^’符号,x^y,会将两个十进制数在二进制下进行异或运算(不同为1,其余 为0)然后返回其十进制下的值。例如3(11)^2(10)=1(01)。
  4. ’~’符号,~x,按位取反。例如~101=010。
  5. ’<<’符号,左移操作,x<<2,将x在二进制下的每一位向左移动两位,最右边用0填充,x<<2相当于让x乘以4。 ’>>’符号,是右移操作,x>>1相当于给x/2,去掉x二进制下的最右一位

1.判断一个数字x二进制下第i位是不是等于1。(最低第1位)

方法:if(((1<<(i−1))&x)>0) 将1左移i-1位,相当于制造了一个只有第i位 上是1,其他位上都是0的二进制数。然后与x做与运算,如果结果>0, 说明x第i位上是1,反之则是0。

2.将一个数字x二进制下第i位更改成1。

方法:x=x|(1<<(i−1)) 证明方法与1类似。

3.将一个数字x二进制下第i位更改成0。

方法:x=x&~(1<<(i−1))

4.把一个数字二进制下最靠右的第一个1去掉。

方法:x=x&(x−1)

第i个元素是否已经走过 i&(1<<(i-1))==0

所有元素全都走过,i&((1<<i)-1)==0

中间三层循环有点复杂。要清楚最里面表示的是什么状态。

有1组数据没过。

参考:状压DP(超详细!!!) - endl\n - 博客园 (cnblogs.com)

posted @ 2022-06-14 23:34  辛夸高岭桂  阅读(54)  评论(0)    收藏  举报