蓝桥杯 种花小游戏 java 状压

问题描述

植物大战僵尸这款游戏中,还有个特别有意思的赚钱方式——种花(能长金币的花)。
  种出来的金币需要玩家点击才能得到,或者,玩家可以购买一只蜗牛来帮助捡金币。然而,蜗牛爬得慢是众所周知的。所以,场上有若干金币时,蜗牛总是喜欢以最少的行程来捡走所有的金币。
  现在告诉你场上n个金币所在位置的坐标,以及蜗牛所在位置,让你求出蜗牛捡走所有金币的最小行程。

输入格式

第一行一个正整数n,表示金币数量
  之后n行,每行两个非负整数x、y,分别表示金币所在位置坐标
  最后一行两个正整数x、y表示蜗牛起始位置。

输出格式

一个实数(保留2位小数),表示最短行程

思路

dp[i][j]表示在第i个状态(经过哪些点,状压二进制表示),蜗牛在第j个点时的最短总长度。dp[i][j] = min(distance[m][j]+dp[n,m]) (m为i状态任一位为1的位置,n为i在二进制下m对应位设为0)

package ADV;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.TreeSet;

/**
 *  种花
 *  http://lx.lanqiao.cn/problem.page?gpid=T788
 *	ac
 */
public class ADV383 {
	static String[] s;
	static int n;
	static int[] x;
	static int[] y;
	static int sum;
	static double maxn = 200000000.0;
	static double[][] distance = new double[20][20];
	static double[][] f = new double[70000][17];
	static boolean[] vis;
	
	static TreeSet<Double> treeSet = new TreeSet<>();
	public static void main(String[] args) throws IOException {
		BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
		s = in.readLine().split(" ");
		n = Integer.valueOf(s[0]);
		x = new int[n+1];y = new int[n+1];vis = new boolean[n+1];
		for (int i = 1; i <= n; i++) {
			s = in.readLine().split(" ");
			x[i] = Integer.valueOf(s[0]);
			y[i] = Integer.valueOf(s[1]);
		}
		s = in.readLine().split(" ");
		x[0] = Integer.valueOf(s[0]);
		y[0] = Integer.valueOf(s[1]);
		
		for(int i=0;i<=n;i++) {
			for(int j=i+1;j<=n;j++)
		    {
		    distance[i][j]=countDisd(x[i], y[i], x[j], y[j]);
		    distance[j][i]=distance[i][j];
		    }
		}
		work();
//		search(0, 0);
	}
	static double DP(int now,int st)//递归
	{
	    if(f[now][st]<maxn) return f[now][st];  //记忆化;
	    for(int i=1;i<=n;i++)
	    {
	        if((i!=st)&&((1<<(i-1))&now)!=0) //枚举上一次在哪里;1<<(i-1))&now)!=0意思是now的第i位不是0,即到过i
	        f[now][st]=Math.min(f[now][st],distance[i][st]+DP(now-(1<<(st-1)),i));//now-(1<<(st-1)) st位设为0
	    }
	    return f[now][st];
	}
	static void work()//递归倒着想,以每个金币为起点,经过所有金币来到蜗牛最初位置
	{
		double ans = 0x3f3f3f3f;
	    for(int i=1;i<=n;i++) 
	    sum+=(1<<(i-1));//每个点都走过,用二进制有多少位个1
	    for(int i=1;i<=sum;i++)
	    for(int j=1;j<=n;j++)
	    f[i][j]=maxn;//赋初值;
	    for(int i=1;i<=n;i++)
	    f[(1<<(i-1))][i]=0;//因为是倒着想,每个点都可能作为起始点,总长度0

	    for(int i=1;i<=n;i++)
	    {
	        ans=Math.min(ans,distance[0][i]+DP(sum,i)); //倒着想,走完所有点后从i点到0点
	    }
	    System.out.printf("%.2f",ans);
	}
	
//	static void search(int point,double len) {//暴力搜索 70分
//		boolean flag = false;
//		for (int i = 1; i <= n; i++) {
//			if(vis[i])
//				continue;
//			flag = true;
//			double temp = countDisd(x[point],y[point],x[i],y[i]);
//			vis[i] = true;
//			search(i, len+temp);
//			vis[i] = false;
//		}
//		if(!flag) {
//			treeSet.add(len);
//		}
//	}
	static double countDisd(int x1,int y1,int x2,int y2) {
		return Math.sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
	}
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值