Sweety

Practice makes perfect

导航

蓝桥杯--- 历届试题 邮局 (深搜+暴力)(动态待解决)

Posted on 2015-05-07 21:41  蓝空  阅读(1570)  评论(0编辑  收藏  举报

问题描述
  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。


题目解析:刚拿到题的时候感觉提示好像有点问题吧,怎么会是搜索,但是真正做的时候却明显感觉是多重循环的暴力,稍做剪枝,但是这道题我却做了大概不太到一天的时间,太惨了,啊啊啊啊啊


我却感觉这道题肯定是动态的问题,但是现在试过还是不太好处理,网上的结题报告暂时还没有,之后自己写一个。。。


//一定注意二维数组如果开小了的话,一般不会报错,这么简单的一道题我竟然弄了半天,愁。。。 
#include <iostream>
#include <stdio.h>
#include <queue>
#include <math.h>
#include <string.h>
#define N 10010
using namespace std;
int k,vis[55],tmpvis[55],mark[55];
int n, m;
double dis[55][55];
double sum=9999999999.0;
struct T{
	int x, y;
}post[55],vill[55];
void dfs(int node,int count,double tmpsum,double tmpdis[55]){//node是哪个记录邮局 ,count记录建立邮局个数  tmpsum记录当前的全部距离之和
                                                             //tmpdis 记录每个用户和邮局的最小距离 
	if( k-1-count>m-1-node) return ;//剪枝   之后的节点如果不够||tmpsum>=sum 
	double newdis[55];
	if(node+1<m)
	  dfs(node+1,count,tmpsum,tmpdis);//忽略本邮局,建立下一个邮局 
	for(int i=0;i<n;i++)//记录下当前的数组情况 
	     newdis[i] = tmpdis[i];
	if(count==k-1){//出口,条件满足 
		tmpvis[count]=node; 
		for(int i=0;i<n;i++){//更新最小值  
		  if(newdis[i]>dis[ node ][i]){
		  	int temp=tmpsum; 
		  	 tmpsum=tmpsum-newdis[i]+dis[ node ][i];
		  	 newdis[i]=dis[ node ][i];
		  }  
		} 
		if(tmpsum<sum){//更新输出最小值 
			sum=tmpsum;   
			for(int i=0;i<k;i++){
			   vis[i]=tmpvis[i];
			}
		}	
		return ;
	}
	tmpvis[count]=node;	//加入新的邮局节点 
	int mark1=1,mark2=1; //mark1==1说明为第一次,不可忽略,Mark2==1说明加入该点之后没有发挥作用   ,但是加入这两个标记位之后感觉好像有些情况是不对的,但是测试没有问题 ,并且没有改变90%超时的问题 
	if(count==0){ //如果是建立第一个邮局,初始化邮局到用户距离数组tmpdis和最小值 
		for(int i=0;i<n;i++){
	      tmpsum+=dis[ node ][i];
	      newdis[i]=dis[ node ][i];
		}
		mark1=0;
	 }
	else{//判断是否有距离新加的节点更近的节点,如果有加入并且更新 
		for(int i=0;i<n;i++){
		  if(newdis[i]>dis[ node ][i])	{
		  	 int temp=tmpsum; 
		  	 tmpsum=tmpsum-newdis[i]+dis[ node ][i];
			 newdis[i]=dis[ node ][i];
		  	 mark2=0;
		  }
	    }
	}
	////////if(tmpsum>sum) return ;//不可以这样剪枝,因为即便现在比sum大了,但是之后可能遇到更短的节点,一样可以更小 
	if(!(mark1&&mark2))	
  	  if(node+1<m)
	   dfs(node+1,count+1,tmpsum,newdis );
	return ;
}
int main (){
	while(~scanf("%d%d%d",&n,&m,&k)){
		sum=9999999999.0;
		for(int i=0;i<n;i++)
		  scanf("%d%d",&vill[i].x,&vill[i].y);
		for(int i=0;i<m;i++){
			scanf("%d%d",&post[i].x,&post[i].y);
			for(int j=0;j<n;j++){
			  dis[i][j]=sqrt( pow(vill[j].x-post[i].x,2)+pow(vill[j].y-post[i].y,2) );	
			}   
		}
		double tmpdis[55];
		   dfs(0,0,0,tmpdis); 	
		for(int i=0;i<k;i++)
		    printf("%d ",vis[i]+1);
		cout<<endl;
	}
    return 0;
}