P1433 吃奶酪(状压dp)

洛谷 / 题目列表 / 题目详情
P1433 吃奶酪
提交
23.28k
通过
9.30k
时间限制
1.00s
内存限制
125.00MB
题目描述
房间里放着n块奶酪。一只小老鼠要把它们都吃掉,问至少要跑多少距离?老鼠一开始在(0,0)点处。

输入格式
第一行一个数n (n<=15)

接下来每行2个实数,表示第i块奶酪的坐标。

两点之间的距离公式=sqrt((x1-x2)(x1-x2)+(y1-y2)(y1-y2))

输出格式
一个数,表示要跑的最少距离,保留2位小数。

输入输出样例
输入 #1 复制

4
1 1
1 -1
-1 1
-1 -1

输出 #1 复制

7.41

···
/*
预处理一下各个点之间的距离,
暴搜可以过
但是!!
这是经典的NP问题,
数据比较小,用状压dp,d它!!!!

*/
第一种写法:
虚拟一个终点(原来的终点到它的距离为0,
记的起点(0,0)到其他点的距离要算上),
就相当于前几天写的:最短Hamilton路径(状压dp)

#include <bits/stdc++.h>
using namespace std;
const int N = 1<<20;
double dp[N][20];
struct Point
{
    double x,y;
} p[20];
double getDis(int i,int j)
{
    return sqrt(pow(p[i].x-p[j].x,2)+pow(p[i].y-p[j].y,2));
}
double dis[20][20];
int n;
void init()
{
    for(int i = 0; i <= n; i++)
    {
        for(int j = i; j <= n; j++)
        {
            dis[i][j] = dis[j][i] = getDis(i,j);
        }
    }
    int t = n+2;
    for(int i = 0; i < (1<<t); i++)
    {
        for(int j = 0; j < t; j++)
        {
            dp[i][j] = 1e16;
        }
    }
    dp[1][0] = 0;
}
int main()
{
    cin>>n;
    p[0].x = p[0].y = 0;
    for(int i = 1; i <= n; i++)
    {
        cin>>p[i].x>>p[i].y;
    }
    init();
    n = n+2;//原来起点(0,0),还有虚拟了一个(原来所有终点的)终点
    for(int i = 1; i < (1<<n); i++)
    {
        for(int j = 0; j < n; j++)
        {
            if((i>>j)&1)
                for(int k = 0; k < n; k++)
                {
                    dp[i][j] = min(dp[i][j],dp[i-(1<<j)][k]+dis[k][j]);
                }
        }
    }
    cout.precision(2);
    cout<<fixed<<dp[(1<<n)-1][n-1]<<endl;
    return 0;
}

为了更深入了解状压dp,
第二种写法,不虚拟终点,
最终对所有终点的求个最小值
(时间,空间复杂度都更优)

#include <bits/stdc++.h>
using namespace std;
const int N = 1<<17;
double dp[N][17];
struct Point
{
    double x,y;
} p[17];
double getDis(int i,int j)
{
    return sqrt(pow(p[i].x-p[j].x,2)+pow(p[i].y-p[j].y,2));
}
double dis[17][17];
int n;
void init()
{
    for(int i = 0; i <= n; i++)
    {
        for(int j = i; j <= n; j++)
        {
            dis[i][j] = dis[j][i] = getDis(i,j);
        }
    }
    int t = n+1;
    for(int i = 0; i < (1<<t); i++)
    {
        for(int j = 0; j < t; j++)
        {
            dp[i][j] = 1e16;
        }
    }
    dp[1][0] = 0;
}
int main()
{
    cin>>n;
    if(!n)
    {
        cout<<"0.00"<<endl;
        return 0;
    }
    p[0].x = p[0].y = 0;
    for(int i = 1; i <= n; i++)
    {
        cin>>p[i].x>>p[i].y;
    }
    init();
    n = n+1;
    for(int i = 1; i < (1<<n); i++)
    {
        for(int j = 1; j < n; j++)
        {
            if((i>>j)&1)
                for(int k = 0; k < n; k++)
                {
                    dp[i][j] = min(dp[i][j],dp[i-(1<<j)][k]+dis[k][j]);
                }
        }
    }
    double ans = 1e16;
    for(int i = 1; i < n; i++)
        ans = min(ans,dp[(1<<n)-1][i]);
    cout.precision(2);
    cout<<fixed<<ans<<endl;
    return 0;
}
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Leo Bliss

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值