题目背景
带我原好不好!
题目描述
蒙德城有一个巨大的圆形广场,为了绿化环境和净化空气,西风骑士团决定沿圆形广场外圈种一圈树。
骑士凯亚得到指令后,初步规划出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!
提示
![](https://img-blog.csdnimg.cn/img_convert/92741828a0347e1f082a51dd6704cfc8.png)
1.解题思路:
双向链表、优先队列、反悔贪心
2.解决方案
用反悔贪心每次求局部最优,但有时局部最优并不能保证全局最优,故增加反悔机制。
例:4 2
8 9 8 1
![](https://img-blog.csdnimg.cn/img_convert/65600cf5d2f1b79b6a3b5eef56101eea.jpeg)
图1-1
![](https://img-blog.csdnimg.cn/img_convert/40516c45193109d8d55e882d48d8c484.jpeg)
图1-2
![](https://img-blog.csdnimg.cn/img_convert/8fbfdd52595d30f4c84e676d81e8857a.jpeg)
图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;
}
}