1)问题描述
多边形游戏是一个单人玩的游戏,开始时有一个由n个顶点构成的多边形。每个顶点被赋予一个整数值,每条边被赋予一个运算符+或*。所有边依次用整数从1到n编号,游戏第1步,将一条边删除。
随后n-1步按以下方式操作:
(1)选择一条边E以及由E连接着的两个顶点V1和V2;
(2)用一个新的顶点取代边E以及由E连接着的两个顶点V1和V2。将由顶点V1和V2 的整数值通过边E上的运算得到的结果赋予新顶点。
最后,所有边都被删除(断开),游戏结束。游戏的得分就是所剩顶点上的整数值。问题:对于给定的多边形,计算最高分。
2)代码实现
package hello;
import java.util.HashMap;
import java.util.Scanner;
import java.util.Stack;
public class fuck {
private int n; //多边形边数
private char[] op; //每条边的对应的操作(从1开始计数)
private int[] v; //每个顶点数值(从1开始计数)
private long[][][] m; //m[i][n][1]:代表一开始删除第i条边,长度为n的链(包含n个顶点),所能得到的最大值
//m[i][n][0]:代表一开始删除第i条边,长度为n的链,所能得到的最小值
private int[][][] cut; //cut[i][j][0];表示m[i][j][0]这条链的达到最小数值的时候断开的位置
//cut[i][j][1]: 表示m[i][j][1]这条链的达到最大数值的时候断开的位置
private Stack<Integer> stack; //用栈保存合并边的顺序
private int firstDelEdge; //记录最优情况下,第1条删除的边
private long bestScore; //记录最优得分
//初始化
public fuck(int n, long[][][] m, char[] op, int[] v){
this.n = n;
this.m = m;
this.op = op;
this.v = v;
this.cut = new int[n+1][n+1][2];
this.stack = new Stack<>();
}
//************************************************************************************************************************************
/**
* 子函数
* 服务于主执行函数,就是主函数的一些方法的封装
*/
//把断开的边是*还是+的时候计算最大值和最小值封装在一起,向外暴露一个统一的方法
private HashMap<String, Long> minMax(int i, int s, int j, HashMap<String, Long> resMap){
int r = (i+s-1) % n + 1;
long a = m[i][s][0], b = m[i][s][1], c = m[r][j-s][0], d = m[r][j-s][1];
if(op[r] == '+'){
resMap.put("minf", a+c);
resMap.put("maxf", b+d);
}else{
long[] e = new long[]{0, a*c, a*d, b*c, b*d};
long minf = e[1], maxf = e[1];
for (int k = 2; k < 5; k++){
if(minf > e[k]) minf = e[k];
if(maxf < e[k]) maxf = e[k];
}
resMap.put("minf", minf);
resMap.put("maxf", maxf);
}
return resMap;
}
/**
* 获取最优的合并序列,存入stack中
* @param i 表示子链从哪个顶点开始
* @param j 子链的长度(如j=2,表示链中有两个顶点)
* @param needMax 是否取链的最大值,如果传入值为false,则取子链的最小值
*/
//算出子链m[i][j][1(0)]取到最大(小)值的时候要断开的点,并压栈,在需要的时候出栈。
//递归压栈到子链的长度为1(即j为1的时候)。
private void getBestSolution(int i, int j, boolean needMax){
//needMax为true的话就是把要取子链的最大值的时候断开的点压栈,为false就是把要取子链的最小值的时候断开的点压栈
int s,r;
if(j == 1){ //链中只有一个顶点,直接返回
}else if(j == 2){
s = cut[i][j][1];
r = (i+s-1) % n + 1; //因为压栈的r是相对于最开始的,而s是相对于i的。
stack.push(r);
}else { //链中有两个以上的顶点时,将最优的边入栈
s = needMax ? cut[i][j][1] : cut[i][j][0];
r = (i+s-1) % n + 1;
stack.push(r);
//当j>2的时候的递归操作
if(this.op[r] == '+'){ //当合并计算为"+"操作时
if(needMax){ //如果合并得到的父链需要取得最大值
getBestSolution(i, s, true);
getBestSolution(r, j-s, true);
}else { //如果合并得到的父链需要取得最小值
getBestSolution(i, s, false);
getBestSolution(r, j-s, false);
}
}else{ //当合并计算为"*"操作时
long a = m[i][s][0], b = m[i][s][1], c = m[r][j-s][0], d = m[r][j-s][1];
long[] e = new long[]{0, a*c, a*d, b*c, b*d};
long mergeMax = e[1], mergeMin = e[1];
for(int k=2; k<=4; k++){
if(e[k] > mergeMax) mergeMax = e[k];
if(e[k] < mergeMin) mergeMin = e[k];
}
long merge = ((needMax) ? mergeMax : mergeMin); //判断合并得到的父链是取最大还是取最小
if(merge == e[1]){ //子链1和子链2都取最小
getBestSolution(i, s, false);
getBestSolution(r, j-s, false);
}else if(merge == e[2]){ //子链1取最小,子链2取最大
getBestSolution(i, s, false);
getBestSolution(r, j-s, true);
}else if(merge == e[3]){ //子链1取最大,子链2取最小
getBestSolution(i, s, true);
getBestSolution(r, j-s, false);
}else { //子链1和子链2都取最大
getBestSolution(i, s, true);
getBestSolution(r, j-s, true);
}
}
}
}
//************************************************************************************************************************************
/**
* 主执行函数
* 就是主要的算法的核心
*/
//算出断开哪个的点的时候最值并把最值填入m[i][j][0] 和 m[i][j][1];把断开的点填入cut[i][j][0]和cut[i][j][1]
private void polyMax(){
//1 填表 m[i][j][0] 和 m[i][j][1] 和 cut[i][j][0] 和 cut[i][j][1]
HashMap<String, Long> resMap = new HashMap<>();
for (int j = 2; j <= n; j++){ //链的长度
for(int i = 1; i<= n; i++){ //一开始断开第i条边的时候
m[i][j][0] = Long.MAX_VALUE;
m[i][j][1] = Long.MIN_VALUE;
for(int s = 1; s < j; s++){ //断开的位置
resMap = this.minMax(i, s, j, resMap);
if(m[i][j][0] > resMap.get("minf")){
m[i][j][0] = resMap.get("minf");
cut[i][j][0] = s; //记录该链取得最小值的断点
}
if(m[i][j][1] < resMap.get("maxf")){
m[i][j][1] = resMap.get("maxf");
cut[i][j][1] = s; //记录该链取得最大值的断点
}
}
}
}
//2 根据表m算出第一次断开哪里的时候的数值最大,并输出一些相关数据
bestScore = m[1][n][1];
firstDelEdge = 1; //一开始断开的边,初始化为第一条边
for (int i = 2; i <= n; i++){
if(bestScore < m[i][n][1]){
bestScore = m[i][n][1];
firstDelEdge = i; //如果一开始断开第i边有更优的结果,则更新
}
}
System.out.print("\n");
System.out.println("一开始断开第i条边时可以形成的最大数值:");
for(int i=1; i<=n; i++){ //一开始断开第i条边所能得到的最大分数
System.out.println("i=" + i + " " + m[i][n][1]);
}
System.out.print("\n");
System.out.println("【第一次应该断开的边为:firstDelEdge=" + firstDelEdge+"】");
//3 利用getBestSolution方法算出达到最大数值的时候的断开顺序并输出
System.out.print("\n");
getBestSolution(firstDelEdge, n, true); //把子链m[firstDelEdge][n]要取最大值的时候要断开的点依次压栈。
System.out.println("要想得到最大的数断开的顺序为:");
System.out.println("stack--> "+ firstDelEdge);
while (!stack.empty()){ //打印在断开第firstDelEdge条边后的最优合并顺序
System.out.println("stack--> " + String.valueOf(stack.pop()));
}
System.out.print("\n");
System.out.println("【按以上断开顺序后得到的数(最大的数值)为:BestScore=" + bestScore+"】");
}
//************************************************************************************************************************************
/**
* 主函数
* @param args
*/
public static void main(String[] args){
System.out.println("请输入你要输入的边(点)的个数:");
Scanner scanner = new Scanner(System.in);
while(scanner.hasNext()){
//1 输入流
int n = scanner.nextInt();
long[][][] m = new long[n+1][n+1][2];
char[] op = new char[n+1];
int[] v = new int[n+1];
System.out.print("\n");
System.out.println("请输入边和点:");
for(int i=1; i<=n; i++){ //i从1开始
op[i] = scanner.next().charAt(0);
v[i] = scanner.nextInt();
}
//2 初始化
fuck ploygonAgent = new fuck(n, m, op, v);
for (int i=1; i<=n; i++){ //初始化m[i][j][0]和m[i][j][1]的第一列
m[i][1][0] = m[i][1][1] = v[i];
}
//3 主执行函数
ploygonAgent.polyMax();
}
scanner.close();
}
}
请输入你要输入的边(点)的个数:
5
请输入边和点:
* -5 + -2 * -8 * -5 + 8
一开始断开第i条边时可以形成的最大数值:
i=1 168
i=2 480
i=3 488
i=4 488
i=5 120
【第一次应该断开的边为:firstDelEdge=3】
要想得到最大的数断开的顺序为:
stack--> 3
stack--> 2
stack--> 1
stack--> 5
stack--> 4
【按以上断开顺序后得到的数(最大的数值)为:BestScore=488】
3)时间复杂度和空间复杂度
时间复杂度:
O(n*3)
【主要的时间复杂度就在于那填表的三个for】
空间复杂度:
O(n*2)
来源:CSDN
作者:weixin_44721537
链接:https://blog.csdn.net/weixin_44721537/article/details/104087103