问题描述
植物大战僵尸这款游戏中,还有个特别有意思的赚钱方式——种花(能长金币的花)。
种出来的金币需要玩家点击才能得到,或者,玩家可以购买一只蜗牛来帮助捡金币。然而,蜗牛爬得慢是众所周知的。所以,场上有若干金币时,蜗牛总是喜欢以最少的行程来捡走所有的金币。
现在告诉你场上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));
}
}