A. 一般图最小匹配
看大家都是用 DP 写的,这里提供一种反悔贪心的思路。
将 排序。
注意到对于一个 ,它连出的边权最小的边必然是 。
设 ,最终答案一定是这 个元素选出 个元素。
有一个限制,每个节点只能在匹配中选择 次。也就是当选择了 之后, 与 都不能再被选择。
考虑如果选择 后不优,那么更优解一定同时选择了 和 。这个可以通过一次反悔来修正。
具体地,用一个堆维护全局最小值,选择了 之后,用 来代替 。同时要让新节点成为 的后继, 的前驱,这个可以用链表来实现。
复杂度 。
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define SET(a,b) memset(a,b,sizeof(a))
int read() {
int a=0, f=1; char c=getchar();
while(!isdigit(c)) {
if(c=='-') f=-1;
c=getchar();
}
while(isdigit(c)) a=a*10+c-'0', c=getchar();
return a*f;
}
const int N=5005, inf=1e15;
struct node {
int val, id;
};
bool operator<(node a,node b) {
return a.val>b.val;
};
int n, m, cnt, ans, a[N], v[N], pre[N], nxt[N], d[N];
priority_queue<node> q;
void update(node x) {
int l=pre[x.id], r=nxt[x.id];
v[l]=v[r]=1;
pre[x.id]=pre[l], nxt[pre[l]]=x.id;
nxt[x.id]=nxt[r], pre[nxt[r]]=x.id;
d[x.id]=(l&&r)? min(d[l]+d[r]-d[x.id],inf):inf;
// 选择d[1]或d[n-1]后不可能再反悔,设置成inf
q.push((node){d[x.id],x.id});
}
signed main() {
n=read(), m=read();
for(int i=1;i<=n;++i) a[i]=read();
sort(a+1,a+n+1);
for(int i=1;i<n;++i) d[i]=abs(a[i+1]-a[i]), pre[i]=i-1, nxt[i]=i+1;
for(int i=1;i<n;++i) q.push((node){d[i],i});
nxt[n-1]=0;
for(int i=1;i<=m;++i) {
while(v[q.top().id]) q.pop();
node x=q.top(); q.pop();
ans+=x.val;
update(x);
}
printf("%lld\n",ans);
}