种树问题(Java)

题目背景

带我原好不好!

题目描述

蒙德城有一个巨大的圆形广场,为了绿化环境和净化空气,西风骑士团决定沿圆形广场外圈种一圈树。

骑士凯亚得到指令后,初步规划出n个种树的位置,顺时针编号 1到 n。并且每个位置都有一个美观度

Ai,如果在这里种树就可以得到这 Ai的美观度。但由于蒙德城土壤肥力欠佳,两棵树决不能种在相邻

的位置( i号位置和 i+ 1号位置叫相邻位置。值得注意的是1 号和 n号也算相邻位置)。

最终凯亚给骑士团提供了 棵树苗并要求全部种上,请冒险家帮忙设计种树方案使得美观度总和最大。

如果无法将 棵树苗全部种上,给出无解信息。

输入格式

输入的第一行包含两个正整数n ,m 。

第二行 n个整数,i第 个代表 Ai

输出格式

输出一个整数,表示最佳植树方案可以得到的美观度。如果无解输出 Error! 。

样例 #1

样例输入 #1

7 3

1 2 3 4 5 6 7

样例输出 #1

15

样例 #2

样例输入 #2

7 4

1 2 3 4 5 6 7

样例输出 #2

Error!

提示

1.解题思路:

双向链表、优先队列、反悔贪心

2.解决方案

用反悔贪心每次求局部最优,但有时局部最优并不能保证全局最优,故增加反悔机制。

例:4 2

8 9 8 1

图1-1

图1-2

图3-3

如图1-1,若按照局部最优解来计算的话则9出队列(出队列意味着planted!),接着将9的前后位置标记为planted,再次循环得到8但此时8已经被标记,直接出队(另外一个8也是如此)这样得到的结果为9 + 1 = 10显然不正确。

但如果我们将9出队后再将8 + 8 - 9 = 7入队,结果就变了,7入队后队列里美观度最大的依然是两个8但它们在9出队后就已经被标记,所以两个8直接出队,再次遍历得到7,7未被标记则最终结果为9 + 7 = 16与答案相符,这里的9 + 7就相当于9 + 8 + 8 - 9也就相当于8 + 8。

但是与直接将两个8相加作为结果相比,上述方法还存在一点问题,如图1-2在7出队后被标记的仍然是两个美观度为8的位置,我们再看看直接将两个8相加作为结果的情况如图1-3,此时显然美观度为1的位置也被标记了这就与上述方法出现差异。

解决方案:将7的pre(前一个结点)改为7的pre的pre,将7的next改为7的next的next,再将7的pre的next改为7的下标,将7的next的pre改为7的下标即可,说白了就是修改指针指向,使其与直接将两个8相加作为结果完全相同。

将每次的得到的结果相加即为答案。

3.源代码

import java.util.Arrays;

import java.util.PriorityQueue;

import java.util.Scanner;

public class Main {

static int n;

static int m;

static int blevel[]; //美观度

static DoubleLinkList list[];

static PriorityQueue<DoubleLinkList> pq = new PriorityQueue<>(); //优先队列

public static void main(String[] args) {

Scanner sc = new Scanner(System.in);

n = sc.nextInt();

m = sc.nextInt();

blevel = new int[n];

list = new DoubleLinkList[n];

for (int i = 0; i < n; i++) {

blevel[i] = sc.nextInt();

}

//特判:如果种不下就输出Error!

if (n / 2 < m) {

System.out.println("Error!");

return;

}

//链表初始化

for (int i = 0; i < n; i++) {

list[i] = new DoubleLinkList();

list[i].pre = (i == 0) ? n - 1 : i - 1;

list[i].value = blevel[i];

list[i].next = (i == n - 1) ? 0 : i + 1;

list[i].index = i;

pq.add(list[i]);

}

int max = 0;

Boolean planted[] = new Boolean[n];

Arrays.fill(planted, false);

while (m-- > 0) {

//若队首元素被标记直接出队。

while (planted[pq.peek().index]) {

pq.poll();

}

DoubleLinkList temp = pq.poll();

max += temp.value;

planted[temp.pre] = true;

planted[temp.next] = true;

//反悔机制

temp.value =

list[temp.pre].value + list[temp.next].value - temp.value;

pq.add(temp);

temp.pre = list[temp.pre].pre;

temp.next = list[temp.next].next;

list[temp.pre].next = temp.index;

list[temp.next].pre = temp.index;

}

System.out.println(max);

}

}

class DoubleLinkList implements Comparable<DoubleLinkList> {

int pre;

int value;

int next;

int index;

//重写compareTo方法使队列队首始终为美观度最大的元素

@Override

public int compareTo(DoubleLinkList list) {

return list.value - this.value;

}

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值