第十一届蓝桥杯(国赛)——补给

题目描述
小蓝是一个直升飞机驾驶员,他负责给山区的 n n n 个村庄运送物资。

每个月,他都要到每个村庄至少一次,可以多于一次,将村庄需要的物资运送过去。

每个村庄都正好有一个直升机场,每两个村庄之间的路程都正好是村庄之间的直线距离。

由于直升机的油箱大小有限,小蓝单次飞行的距离不能超过 D D D。每个直升机场都有加油站,可以给直升机加满油。

每个月,小蓝都是从总部出发,给各个村庄运送完物资后回到总部。如果方便,小蓝中途也可以经过总部来加油。

总部位于编号为 1 1 1 的村庄。

请问,要完成一个月的任务,小蓝至少要飞行多长距离?

输入描述
输入的第一行包含两个整数 n , D n, D n,D,分别表示村庄的数量和单次飞行的距离。
接下来 n n n 行描述村庄的位置,其中第 i i i 行两个整数 x i , y i x_i , y_i xi,yi ,表示编号为 i i i 的村庄的坐标。

村庄 i i i 和村庄 j j j 之间的距离为 ( x i − x j ) 2 + ( y i − y j ) 2 \sqrt{(x_i-x_j)^2+(y_i-y_j)^2} (xixj)2+(yiyj)2

输出格式
输出一行,包含一个实数,四舍五入保留正好 2 2 2 位小数,表示答案。

样例输入
4 6
1 1
4 5
8 5
11 1

样例输出
28.00

数据范围
1 ≤ n ≤ 20 , 1 ≤ x i , y i ≤ 1 0 4 , 1 ≤ D ≤ 1 0 5 1 ≤ n ≤ 20, 1 ≤ x_i, y_i ≤ 10^4, 1 ≤ D ≤ 10^5 1n20,1xi,yi104,1D105


题解
状压DP + 最短路径:

w[i][j]:从村庄 i 到村庄 j 之间的最短距离;
f[i][j]:从村庄 0 走到村庄 j ,且经过经过村庄的状态为 i 的最小飞行距离(将 1 映射成 0,以此类推);

#include <bits/stdc++.h>
using namespace std;

typedef pair<int, int> PII;

const int N = 20, INF = 1e9;

PII p[N];
double w[N][N];
double f[1 << N][N];

double get_distance(int i, int j)
{
    int x = p[i].first - p[j].first;
    int y = p[i].second - p[j].second;
    return sqrt(x * x + y * y);
}

int main()
{
    int n, d;
    cin >> n >> d;
    for (int i = 0; i < n; i ++) cin >> p[i].first >> p[i].second;
    
    for (int i = 0; i < n; i ++)
        for (int j = i + 1; j < n; j ++)
        {
            w[i][j] = w[j][i] = get_distance(i, j);
            if(w[i][j] > d) w[i][j] = w[j][i] = INF;
        }
        
    for (int k = 0; k < n; k ++)
        for (int i = 0; i < n; i ++)
            for (int j = 0; j < n; j ++)
                w[i][j] = min(w[i][j], w[i][k] + w[k][j]);
                
    for (int i = 0; i < 1 << n; i ++)
        for (int j = 0; j < n; j ++)
            f[i][j] = INF;
            
    f[1][0] = 0;
    for (int i = 0; i < 1 << n; i ++)
        for (int j = 0; j < n; j ++)
            if(i >> j & 1)
                for (int k = 0; k < n; k ++)
                    if((i - (1 << j)) >> k & 1)
                        f[i][j] = min(f[i][j], f[i - (1 << j)][k] + w[k][j]);
                        
    double ans = INF;
    for (int i = 1; i < n; i ++)
        ans = min(ans, f[(1 << n) - 1][i] + w[i][0]);
        
    printf("%.2f", round(ans * 100) / 100);
    return 0;
}

ps:

  • 然而这样写会超内存,2e7 × 8 ÷ 1024 ÷ 1024 = 152 MB,而题目的内存上限为 128 MB
  • 后来看了别人的题解,就是将 double 转换成 int,这样能节约一半的空间
#include <bits/stdc++.h>
using namespace std;

typedef pair<int, int> PII;

const int N = 20, INF = 1e9;

PII p[N];
int w[N][N], f[1 << N][N];

double get_distance(int i, int j)
{
    int x = p[i].first - p[j].first;
    int y = p[i].second - p[j].second;
    return sqrt(x * x + y * y);
}

int main()
{
    int n, d;
    cin >> n >> d;
    for (int i = 0; i < n; i ++) cin >> p[i].first >> p[i].second;
    
    for (int i = 0; i < n; i ++)
        for (int j = i + 1; j < n; j ++)
        {
            w[i][j] = w[j][i] = get_distance(i, j) * 10000;
            if(w[i][j] > d * 10000) w[i][j] = w[j][i] = INF;
        }
        
    for (int k = 0; k < n; k ++)
        for (int i = 0; i < n; i ++)
            for (int j = 0; j < n; j ++)
                w[i][j] = min(w[i][j], w[i][k] + w[k][j]);
                
    for (int i = 0; i < 1 << n; i ++)
        for (int j = 0; j < n; j ++)
            f[i][j] = INF;
            
    f[1][0] = 0;
    for (int i = 0; i < 1 << n; i ++)
        for (int j = 0; j < n; j ++)
            if(i >> j & 1)
                for (int k = 0; k < n; k ++)
                    if((i - (1 << j)) >> k & 1)
                        f[i][j] = min(f[i][j], f[i - (1 << j)][k] + w[k][j]);
                        
    int ans = INF;
    for (int i = 1; i < n; i ++)
        ans = min(ans, w[i][0] + f[(1 << n) - 1][i]);
        
    printf("%.2f", ans / 10000.0);
    return 0;
}

蓝桥杯C/C++国赛历年题

  • 6
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 10
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值