历届试题 邮局(dfs+剪枝)

  历届试题 邮局  
时间限制:1.0s   内存限制:256.0MB
    
问题描述
  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。

感觉这道题还是很不错的,刚开始没怎么看数据量,写的代码超时了

超时思路:m个备用邮局,建k个,如果用1,0表示建跟不建,那么数组中就有k个1,m-k个0,所以全排列之后就是每一个方案,然后计算出每一组方案的话费,之后取最优花费,但是有的数据比较大,比如50,如果有10个邮局,那么方案就有很多很多,组合数公式算下来肯定崩,但是这个思路对于小的数据应该还是可以的,先上超时代码:

#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define INF 0x3f3f3f3f
struct node
{
	double x,y;
}a[60],b[60];
int n,m,k,s[60],num[60];
double dis(int x,int y)
{
	return sqrt((a[x].x-b[y].x)*(a[x].x-b[y].x)+(a[x].y-b[y].y)*(a[x].y-b[y].y));
}
double dfs()
{
	double sum=0;
	for(int i=0;i<n;i++)
	{
		double val=INF;
		for(int j=0;j<m;j++)
		{
			if(s[j])
			{
				double d=dis(i,j);
				val=min(val,d);
			}
		}
		sum+=val;
	}
	return sum;
}
int main()
{
	while(cin>>n>>m>>k)
	{
		memset(s,0,sizeof(s));
		for(int i=0;i<n;i++)
			cin>>a[i].x>>a[i].y;
		for(int i=0;i<m;i++)
			cin>>b[i].x>>b[i].y;
		for(int i=0;i<k;i++)
		s[i]=1;
		int cnt;
		double ans=INF;
		do{
			double si=dfs();
			if(ans>si)
			{
				ans=si; 
				memset(num,0,sizeof(num));
				cnt=0;
				for(int i=0;i<m;i++)
				if(s[i])
				num[cnt++]=i;
			}
		}while(prev_permutation(s,s+m));
		for(int i=0;i<k-1;i++)
		printf("%d ",num[i]+1);
		printf("%d
",num[k-1]+1);
	}
	return 0;
}
</pre>AC思路(dfs+剪枝):dfs最重要的就是建立搜索树,这里每个点都有两种状态,如果直接暴力时间肯定很长,所以需要加上剪枝,我们可以将村子的住户还有备用邮局看作是两个集合,就像普利姆的思路一样,总的思路就是更新最短距离数组,我们先将所有的点全部连接在第一个备用邮局,也可以不连第一个,毕竟每个点有两种状态,如果第二个备用邮局到住户的距离足以更新最短距离数组,那就更新之后寻找下一个邮局,如果不足以更新最短距离数组,我们就直接放弃这个点,显然他是没有用的,找到k个邮局之后就判定一下话费,是否更短,如果是,更新序号数组
#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)
{//t±íʾѰÕҵĵÚi¸öÓʾ֣¬t±íʾ±¸ÓÃÓʾֵıàºÅ 
    if(i<=m+1)
    {
        if(t==k)//ÕÒµ½ÁË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);//Ò»¸öµãÓÐÁ½ÖÖ״̬£¬Õâ¸öÊDz»½¨Óʾֵģ¬
			//½øÐÐÁËÕâÒ»²½£¬i²»²ÎÓë¸üР
			f1=1,f2=0;//ÅжÏiÊÇ·ñ¿ÉÒÔʹÓà 
            if(!f[i])
            {
                o[t]=i;
	            if(t>0)
	            {
	                f2=1;//Èç¹ûi²»ÊǵÚ0¸öµã 
	                for(j=1;j<=n;j++)
	                {
	                    if(ww[j]>yc[i][j])
	                    {
	                        sum=sum-ww[j]+yc[i][j];//sum´¢´æ×ܵľàÀë 
	                        ww[j]=yc[i][j];
	                        f1=0;//ww±»¸üеıê¼Ç 
	                    }
	                }
	            }
	            else
	            {
	                for(j=1;j<=n;j++)
	                {
	                    sum+=yc[i][j];//¸Õ¿ªÊ¼Ê±È«²¿Á¬ÔÚµÚ1¸öµã¡£Ö®ºó¸üР
	                    ww[j]=w[j]=yc[i][j];
	                }
	        	}
	            if(f1&&f2)//¼ôÖ¦ 
	            {
	                f[i]=1;//²»ÊǵÚ0¸öµãÇÒ²»ÄܲÎÓë¸üР
	                dfs(t,i+1,o,w,sum);//Èç¹ûµÚi¸öÓʾֵ½ÆäËûn¸öµãµÄ¾àÀ붼±ÈwwÖеĴó£¬iÉáÆú 
	            }
	            else
	            dfs(t+1,i+1,o,ww,sum);//µÚi¸öµã¿ÉÒÔ¸üÐÂww¾ÍʹÓøõã
            }
        }
    }
}
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;
}




原文地址:https://www.cnblogs.com/playboy307/p/5273439.html