邮局 DFS 最短路思想

问题 1457: [蓝桥杯][历届试题]邮局

时间限制: 1Sec 内存限制: 128MB 提交: 136 解决: 25

题目描述

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

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

数据规模和约定
对于100%的数据,1< =n< =50,1< =m< =25,1< =k< =10。
 

输入

输入的第一行包含三个整数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

思路:

单词的枚举k个邮局肯定是会超时的。参考了被人博客,发现了新思路。

设状态为 当前枚举的是第x个备选邮局,有选和不选两种选择。 当前已经选了knum个邮局。当前的最小值是s。当前的每个村庄到某个邮局的最小值。

一种类似于最短路的松弛思想,一开始选第一个邮局时,让所有村庄和第一个邮局相连,然后不断枚举新的邮局,看新的邮局能否缩短距离,能的话就继续搜索。

剪枝的话需要注意如果剩余的备选邮局m-x数量小于仍需要安排的邮局k-knum,需要剪枝一下。

代码:

#include<iostream>
#include<algorithm>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define dep(i,a,b) for(int i=a;i>=b;i--)
#define ll long long
#include<string.h>
#include<queue>
#include<stack>
#include<cstdio>
#include<cmath>
using namespace std;
const int maxn=100+6;
#define mod 1000000007
#define INF 0x3f3f3f3f
int dx[]= {-1,1,0,0};
int dy[]= {0,0,-1,1};
int n,m,k;
struct node
{
    int x;
    int y;
} a[maxn],b[maxn];
double dist(node aa,node bb)
{
    return sqrt((aa.x-bb.x)*(aa.x-bb.x)+(aa.y-bb.y)*(aa.y-bb.y));
}
int ans[maxn];
double len[maxn][maxn];
int step[maxn];
double maxx;
bool flag[maxn];
void dfs(int x,int knum,double s,double dis[])
{
    //cout<<x<<endl;
    //cout<<knum<<endl;
    if(knum>k+1)
        return ;
    if(m-x<k-knum)
        return ;//不够了
    if(x>m+1)
        return ;
    if(knum==k+1)
    {
        //cout<<"ss"<<endl;
        if(maxx>s)
        {
            maxx=s;
            rep(i,1,k)
            {
                ans[i]=(step[i]);
            }
        }
        return ;
    }
    double tmp[maxn];
    rep(i,1,n)
    {
        tmp[i]=dis[i];
    }
    dfs(x+1,knum,s,tmp);//不选这个
    if(flag[x])
        return ;
    step[knum]=x;
    //cout<<x<<endl;
    bool f1=0,f2=0;
    if(knum==1)
    {
        rep(i,1,n)
        {
            tmp[i]=len[i][x];
            s+=len[i][x];
        }
        f1=1;
        //cout<<f1<<"rrrr"<<endl;
    }
    else
    {
        rep(i,1,n)
        {
            if(tmp[i]>len[i][x])
            {
                f2=1;
                s+=len[i][x]-tmp[i];
                tmp[i]=len[i][x];
            }
        }
    }

    if(!f1&&!f2)
        flag[x]=1;
    if(f1||f2)
    {
        dfs(x+1,knum+1,s,tmp);//选这个
    }
    return ;
}
int main()
{
    cin>>n>>m>>k;
    rep(i,1,n)cin>>a[i].x>>a[i].y;
    rep(i,1,m)cin>>b[i].x>>b[i].y;//邮局
    rep(i,1,n)
    {
        rep(j,1,m)
        {
            len[i][j]=dist(a[i],b[j]);
        }
    }
    maxx=INF;
    double dis[maxn];
    memset(dis,-1,sizeof(dis));
    dfs(1,1,0,dis);
    for(int i=1; i<=k; i++)
    {
        cout<<ans[i]<<" ";
    }
   // cout<<endl;
    return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值