HDU3921 枚举搜索

4*n个点,每次选4个点,使得距离(射击点---1---2---3---4)最短。问最终将所有点都选完的最小距离

题目给了一个贪心策略,在时间允许范围内搜索即可

枚举任意两点b和c,计算出距离b和距离射击点距离之和最小的点tmp1和次小的点tmp12(并且该点当前未被访问并且不是b c点),再计算出距离c最短的和次短的点tmp2,tmp22,然后更新操作.(分tmp1 == tmp2 和 tmp1 != tmp2两种情况讨论, 求次小是为了tmp1 == tmp2这种情况)

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
#define EPS 1e-4
const int MAXN = 201;
int n;
double ans,mi;
struct Point{
		double x,y;
		void input(){
				scanf("%lf%lf",&x,&y);
		}

}ps[MAXN],o;
inline double pdis(Point a,Point b){
		return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}

int flag[MAXN];
int minodis[MAXN][MAXN];
int mindis[MAXN][MAXN];
double odis[MAXN];
double ppdis[MAXN][MAXN];
int si,ssi,o1,o2,o3,o4;
inline int cmp1(int a,int b){
		return ppdis[a][si] + EPS < ppdis[b][si];
}
inline int cmp2(int a,int b){
		return ppdis[a][ssi] + odis[a] + EPS < ppdis[b][ssi] + odis[b];
}

inline void update(int p1,int p2,int p3,int p4){
	
		double tmp = odis[p1] + ppdis[p1][p2] + ppdis[p2][p3] + ppdis[p3][p4];
		if(tmp+EPS < mi){
			mi = tmp;
			o1 = p1;o2 = p2;o3 = p3;o4 = p4;
		}
		else if(fabs(tmp-mi) < EPS){
			if(odis[p1]+odis[p2]+odis[p3]+odis[p4] < odis[o1]+odis[o2]+odis[o3]+odis[o4]){
				o1 = p1;o2 = p2;o3 = p3;o4 = p4;
			}
		}
}
int main(){
		int cas;
		scanf("%d",&cas);
		for(int kcas = 1; kcas <= cas; ++kcas){
				o.input();
				scanf("%d",&n);
				n = 4*n;
				for(int i = 0; i < n; ++i)ps[i].input();
				for(int i = 0; i < n; ++i){
						odis[i] = pdis(o,ps[i]);
						for(int j = 0; j < n; ++j){
								ppdis[i][j] = pdis(ps[i],ps[j]);
						}
				}
				for(int i = 0; i < n; ++i){
						int cnt = 0;
						for(int j = 0; j < n; ++j){
								if(i == j)continue;
								mindis[i][cnt] = j;
								minodis[i][cnt++] = j;
						}
						si = i;ssi = i;
						sort(mindis[i],mindis[i]+cnt,cmp1);
						sort(minodis[i],minodis[i]+cnt,cmp2);
				}
				ans = 0.0;
				memset(flag,0,sizeof(flag));
				int tmpn = n/4;
				while(tmpn --){
					mi = 1e100;
					for(int i = 0; i < n; ++i){
						if(flag[i])continue;
						for(int j = 0; j < n; ++j){
								if(i == j)continue;
								if(flag[j])continue;
								int t = 0;
								while ((minodis[i][t] == i || minodis[i][t] == j ||flag[minodis[i][t]]))t++;
								int tmp1 = minodis[i][t];
								t++;
								while (minodis[i][t] == i || minodis[i][t] == j ||flag[minodis[i][t]])t++;
								int tmp12 = minodis[i][t];

								t = 0;
								while ((mindis[j][t] == i || mindis[j][t] == j || flag[mindis[j][t]]))t++;
								int tmp2 = mindis[j][t];
								t++;
								while (mindis[j][t] == i || mindis[j][t] == j || flag[mindis[j][t]])t++;
								int tmp22 = mindis[j][t];
								double tmp;
								if(tmp1 == tmp2){
										update(tmp12,i,j,tmp2);
										update(tmp1,i,j,tmp22);
										continue;
								}
								
								update(tmp1,i,j,tmp2);
						}
					}
					ans += mi;
					flag[o1] = flag[o2] = flag[o3] = flag[o4] = 1;
				//	printf("%d %d %d %d\n",o1,o2,o3,o4);
				}
				printf("Case #%d: ",kcas);
				printf("%.2lf\n",ans);
		}
		return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值