经典模型:在n个点中选k个点,要求两两不相邻,且总权值最大/最小。
做法:用双向链表串起来,把所有点丢进堆里,选择一个点的时候把它左右两个点从双向链表和堆中去除,然后把这个点的权值加进ans,出堆后改为左右两边的权值-当前权值重新入堆,重复k次,ans即为答案
原理:左右两边的权值-当前权值相当于这个点不选,改为选左右两边的点,并且多选了一个点,重复k次后必然取到k个点
三道同类型题
bzoj1150:显然一段网线肯定接在相邻的两个点,于是把相邻的点的距离求出来,问题就变成经典模型了
#include<iostream> #include<cstdlib> #include<cstring> #include<cstdio> #include<algorithm> #include<queue> #include<cmath> #include<map> #define ll long long using namespace std; const int maxn=500010,inf=1e9; struct poi{int pos,dis;}; priority_queue<poi>q; bool operator<(poi a,poi b){return a.dis>b.dis;} int n,k,a[maxn],dis[maxn],pre[maxn],next[maxn]; ll ans; bool v[maxn]; void read(int &k) { int f=1;k=0;char c=getchar(); while(c<'0'||c>'9')c=='-'&&(f=-1),c=getchar(); while(c<='9'&&c>='0')k=k*10+c-'0',c=getchar(); k*=f; } inline void del(int x) { int l=pre[x],r=next[x]; pre[x]=next[x]=0; next[l]=r;pre[r]=l; } int main() { read(n);read(k); for(int i=1;i<=n;i++)read(a[i]); for(int i=1;i<n;i++)dis[i]=a[i+1]-a[i],q.push((poi){i,dis[i]});dis[0]=dis[n]=inf; for(int i=1;i<n;i++)pre[i]=i-1,next[i]=i+1; for(int i=1;i<=k;i++) { poi t=q.top(); while(v[t.pos])q.pop(),t=q.top(); ans+=t.dis;v[pre[t.pos]]=v[next[t.pos]]=1; dis[t.pos]=dis[pre[t.pos]]+dis[next[t.pos]]-dis[t.pos]; q.pop();q.push((poi){t.pos,dis[t.pos]}); del(pre[t.pos]);del(next[t.pos]); } printf("%lld\n",ans); return 0; }