蓝桥杯 历届试题 邮局

请往下查看 2017-09-24 修改版

 

问题描述

C村住着n户村民,由于交通闭塞,C村的村民只能通过信件与外界交流。为了方便村民们发信,C村打算在C村建设k个邮局,这样每户村民可以去离自己家最近的邮局发信。

现在给出了m个备选的邮局,请从中选出k个来,使得村民到自己家最近的邮局的距离和最小。其中两点之间的距离定义为两点之间的直线距离。

【输入格式】
输入的第一行包含三个整数n, m, k,分别表示村民的户数、备选的邮局数和要建的邮局数。
接下来n行,每行两个整数x, y,依次表示每户村民家的坐标。
接下来m行,每行包含两个整数x, y,依次表示每个备选邮局的坐标。
在输入中,村民和村民、村民和邮局、邮局和邮局的坐标可能相同,但你应把它们看成不同的村民或邮局。

【输出格式】
输出一行,包含k个整数,从小到大依次表示你选择的备选邮局编号。(备选邮局按输入顺序由1到m编号)

【样例输入】
5 4 2
0 0
2 0
3 1
3 3
1 1
0 1
1 0
2 1
3 2

【样例输出】
2 4

【数据规模和约定】
对于30%的数据,1<=n<=10,1<=m<=10,1<=k<=5;
对于60%的数据,1<=m<=20;
对于100%的数据,1<=n<=50,1<=m<=25,1<=k<=10。

【资源约定】
峰值内存消耗 < 64M
CPU消耗 < 2000ms

本题我用的是深搜加剪枝,利用w[55]数组记录每一步所有用户到已选中的所有邮局中的最小(min)距离,当再次选中一个邮局时,倘若每一用户到此邮局的距离都比w数组里的大,则此邮局不能提供某一用户的最小距离,以后所有计算中都将此邮局舍弃,即剪枝,用标志位的方法舍弃此邮局

代码如下(代码未整理,供参考)

 

#include<iostream>  //邮局  
#include<stdlib.h>
#include<math.h>
using namespace std;
int n,m,k,j,c[55][2],y[27][2],d[12],f1,f2,f[55]={0};
float yc[27][55],s=1000000000;
int dfs(int t,int i,int o[12],float w[55],float sum)
{
    if(i<=m+1)
    {
        if(t==k)
        {
             if(sum<s)
            {
                s=sum;
                for(j=0;j<k;j++)
                    d[j]=o[j];
            }
        }
        else if(i<=m&&t<k)
        {
            float ww[55];
            for( j=1;j<=n;j++)
            ww[j]=w[j];
            dfs(t,i+1,o,w,sum);f1=1,f2=0;
            if(!f[i])
            {
                o[t]=i;
            if(t>0)
            {
                f2=1;
                 for( j=1;j<=n;j++)
                {
                    if(ww[j]>yc[i][j])
                    {
                        sum=sum-ww[j]+yc[i][j];
                        ww[j]=yc[i][j];
                        f1=0;
                    }
                }
            }
            else
             {
                for( j=1;j<=n;j++)
                {
                    sum+=yc[i][j];
                    ww[j]=w[j]=yc[i][j];
                }
            }
            if(f1&&f2)
            {
                f[i]=1;
                dfs(t,i+1,o,w,sum);
            }
            else
            dfs(t+1,i+1,o,ww,sum);
            }
        }
    }
}
int main()
{
    int i,j,o[12];
    float w[55],ww[55];
    cin>>n>>m>>k;
    for(i=1;i<=n;i++)
    cin>>c[i][0]>>c[i][1];
    for(i=1;i<=m;i++)
    {
    cin>>y[i][0]>>y[i][1];
    for(j=1;j<=n;j++)
    yc[i][j]=sqrt((c[j][0]-y[i][0])*(c[j][0]-y[i][0])+(c[j][1]-y[i][1])*(c[j][1]-y[i][1]));
    }
    dfs(0,1,o,w,0);
    for(i=0;i<k;i++)
    cout<<d[i]<<" ";
    return 0;
}

 

2017-09-24 修正版

 

本人工作了, 所以很少登录博客. 偶然看到评论里说代码没AC, 特来修改一下blog. 上面我解释过: 若某备选邮局不能提供某一用户的最小距离, 则剪枝, 但存在一种情况, 就是依次正序访问的备选邮局都能提供最小距离, 此时程序无法做出剪枝处理, 蓝桥杯官方大概就是这样修改了评测用例. 解决方案, 我们可以逆序访问备选邮局, 这样就可以剪枝了. 大家可以看看此次代码和之前的代码, 我修改的核心就是int main()里的 dfs 第二个参数. 所以, 我并没有真正的解决问题, 蓝桥杯官方还可以再把评测用例的顺序改回来, 这样我之前的代码或许有效. 如此, 解决方案大家去想吧. 如今看以前的代码结构真是特烂, 无心改, 大家将就着看吧.

 

剪枝描述

假设我们选中了3个邮局, 每户人家到邮局的最短距离都是1000米, 此时下一个备选邮局到最近居民点的距离是2000米, 这就是不能提供最短距离, 可以直接剪枝; 反例: 下一个备选邮局到最近居民点的距离是999米, 它能提供最短距离, 暂时不能剪枝, 再下一个, 998米, 997米... 这样我们就无法剪枝, 程序一直深搜下去, 结果就超时了, 所以, 我们可以逆序访问备选邮局, 这时, 已选中的邮局到每户人家的距离是997米, 下一个备选邮局是998米, 可以剪枝, 再下一个, 999米... 

 

附测评详情:

 

 

#include<iostream>
#include<math.h>
/*
 * 邮局
 * 代码真烂, 这变量名都是啥, 现在连我本人都看不下去了;
 * 大家忍忍吧...
 */
using namespace std;
int n, m, k, j, c[55][2], y[27][2], retArr[12], f1, f2, f[55] = {0};
float yc[27][55], minCount = 1000000000;

int dfs(int t, int i, int o[12], float w[55], float nowCount) {
    if (i >= 0) {
        if (t == k) {
            if (nowCount < minCount) {
                minCount = nowCount;
                for (j = 0; j < k; j++) {
                    retArr[j] = o[j];
                }
            }
        } else if (i > 0 && t < k) {
            float ww[55];
            for (j = 1; j <= n; j++) {
                ww[j] = w[j];
            }
            dfs(t, i - 1, o, w, nowCount);
            f1 = 1, f2 = 0;
            if (!f[i]) {
                o[t] = i;
                if (t > 0) {
                    f2 = 1;
                    for (j = 1; j <= n; j++) {
                        if (ww[j] > yc[i][j]) {
                            nowCount = nowCount - ww[j] + yc[i][j];
                            ww[j] = yc[i][j];
                            f1 = 0;
                        }
                    }
                } else {
                    for (j = 1; j <= n; j++) {
                        nowCount += yc[i][j];
                        ww[j] = w[j] = yc[i][j];
                    }
                }
                if (f1 && f2) {
                    f[i] = 1;
                    dfs(t, i - 1, o, w, nowCount);
                } else {
                    dfs(t + 1, i - 1, o, ww, nowCount);
                }
            }
        }
    }
}

int main() {
    int i, o[12];
    float w[55];
    cin >> n >> m >> k;
    for (i = 1; i <= n; i++) {
        cin >> c[i][0] >> c[i][1];
    }
    for (i = 1; i <= m; i++) {
        cin >> y[i][0] >> y[i][1];
    }
    for (i = 1; i <= m; i++) {
        for (j = 1; j <= n; j++) {
            yc[i][j] = sqrt((c[j][0] - y[i][0]) * (c[j][0] - y[i][0]) + (c[j][1] - y[i][1]) * (c[j][1] - y[i][1]));
        }
    }
    dfs(0, m, o, w, 0);
    for (i = k - 1; i >= 0; i--) {
        cout << retArr[i] << " ";
    }
}

 

 

 

 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
青蛙跳杯子是蓝桥杯历届试题中的一道典型题目。题目要求模拟一个青蛙跳杯子的游戏,给定n个杯子和m次操作,每次操作可以选择一个杯子,并将其放置在当前位置前面或者后面的位置。最后,要求输出经过m次操作后,杯子从左到右的顺序。 下面是一个可能的Java代码实现: ```java import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); int n = scanner.nextInt(); // 杯子的数量 int m = scanner.nextInt(); // 操作的次数 int[] cups = new int[n]; // 初始化杯子的初始顺序 for (int i = 0; i < n; i++) { cups[i] = i+1; } // 执行m次操作 for (int i = 0; i < m; i++) { int index = scanner.nextInt(); // 选择的杯子的位置 int direction = scanner.nextInt(); // 移动的方向,1表示前面,2表示后面 // 将选择的杯子移动到指定位置 if (direction == 1) { for (int j = index-1; j > 0; j--) { int temp = cups[j]; cups[j] = cups[j-1]; cups[j-1] = temp; } } else if (direction == 2) { for (int j = index-1; j < n-1; j++) { int temp = cups[j]; cups[j] = cups[j+1]; cups[j+1] = temp; } } } // 输出最终的杯子顺序 for (int i = 0; i < n; i++) { System.out.print(cups[i] + " "); } } } ``` 该代码首先读取输入的杯子数量n和操作次数m,然后创建一个数组用来保存杯子的顺序。从1到n依次填充数组的初始顺序。接下来,执行m次操作,根据输入的位置和方向调整杯子的顺序,最后输出调整后的杯子顺序。 需要注意的是,该代码没有进行输入合法性的验证,实际应用中可能需要对输入进行适当的验证和处理。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值