专栏文章

真.调查兵团的种树式 100分解法

题解参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@miqosp9n
此快照首次捕获于
2025/12/04 08:17
3 个月前
此快照最后确认于
2025/12/04 08:17
3 个月前
查看原文

该不会真又有人点了之前的100分做法吧

做法

本题第一个思路是贪心,然而常规贪心无法解决不相邻的问题,所以就有了50分的dp做法,此时我们想到只要建立一个大根堆并保证每次出堆后,根顶元素互不相邻,然后累加权值就可以了。
但这么做显然有问题,所以可以把上述思路优化成如果我先取了一个较大的点后来发现取相邻两个点比只取它一个更优那就取相邻的那两个点,
so:两种情况
  • 取当前第i个点 : ans+=a[i]ans+=a[i]
  • 取第i个点的相邻两点 : ans+=a[i1]+a[i+1]a[i]ans+=a[i-1]+a[i+1]-a[i]
    //再次强调,这里讨论这种情况的前提一定是第i个点已经被取了
我们如何才能取到上述情况呢,可以先把每个aia_i放进堆中,每次取出最大值后 ans+=a[i]ans+=a[i] 再将a[i1]+a[i+1]a[i]a[i-1]+a[i+1]-a[i]放入堆中,如果后来堆顶取到它就代表取相邻两点更优
再再再考虑到可能取相邻的相邻更优所以每次我们将a[i1]+a[i+1]a[i]a[i-1]+a[i+1]-a[i]放入堆中时将aia_i相邻两点打上标记视为删除,将ai=a[i1]+a[i+1]a[i]a_i=a[i-1]+a[i+1]-a[i],更改其余点的相邻情况,确保其他点不会取到aia_i相邻的两个点就行
实现: 可使用链表进行维护
可能讲得相当模糊,大家看代码吧。同大家也可以通过模拟代码,来理解这道题(因为我讲得太烂了)

代码

CPP
#include<cstdio>
#include<queue>
using namespace std;
long long n,m;
long long arr[300005];
struct s{
	long long num;
	long long id;
};
priority_queue <s> q;
bool operator<(s x,s y){
	return x.num<y.num;
}
long long l[300005],r[300005];
long long t[300005];
long long ans;
int main(){
	scanf("%lld%lld",&n,&m);
	for(long long i=1;i<=n;i++){
		scanf("%lld",&arr[i]);
		l[i]=i-1;
		r[i]=i+1;
		q.push({arr[i],i});
	}
	l[n+1]=n;
	r[0]=1;
	for(long long i=1;i<=m;i++){
		while(t[q.top().id]==1){
			q.pop();
		}
		if(q.top().num<=0){
			break;
		} 
		long long wei=q.top().num;
		long long poi=q.top().id;
		q.pop();
		arr[poi]=arr[l[poi]]+arr[r[poi]]-arr[poi];//提供反悔选项 
		q.push({arr[poi],poi});
		t[l[poi]]=1;//标记 
		t[r[poi]]=1;
		l[poi]=l[l[poi]];//更新相邻情况,注意相邻的相邻也要更新 
		r[poi]=r[r[poi]];
		r[l[poi]]=poi;
		l[r[poi]]=poi;
		ans+=wei;
	}
	printf("%lld",ans);
	return 0;
}
讲得很烂,代码也很丑,大家凑合着看吧orz。 分享两个大佬的题解:

题外话

看了大佬们的题解后才发现这类题目叫反悔贪心,贪心思路好似和之前的“糖果”有点像。
个人认为本题贪心策略可以学习,链表维护不相邻的情况也可以学习,但放在一起适用性并不高

评论

0 条评论,欢迎与作者交流。

正在加载评论...