问题 B: 就
时间限制: 1 Sec 内存限制: 512 MB题目描述
就so.in/.out
【背景描述】
一排 N 个数, 第 i 个数是 Ai , 你要找出 K 个不相邻的数, 使得他们的和最大。
请求出这个最大和。
【输入格式】
第一行两个整数 N 和 K。
接下来一行 N 个整数, 第 i 个整数表示 Ai 。
【输出格式】
一行一个整数表示最大和, 请注意答案可能会超过 int 范围
【样例输入】
3 2
4 5 3
【样例输出】
7
【数据范围】
对于 20% 的数据, N, K ≤ 20 。
对于 40% 的数据, N, K ≤ 1000 。
对于 60% 的数据, N, K ≤ 10000 。
对于 100% 的数据, N, K ≤ 100000 , 1 ≤ Ai ≤ 1000000000。
考试时大部分人应该都自豪的写出了n*n的DP,能A60分,结果正解是贪心。对,就是贪心,最先选择最大的一个点,明显这不一定是最优解,而且他周围的两个点都没办法选了,而他的值加进了ans无法再踢除。。
这些问题用一个set+链表就解决了。选了一个点,就把他周围两个点用链表缩成一个,权值赋成左边点权值+右边点权值-自己权值。这个就是用来反悔的。如果选了新的点,相当于向ans里加了A+C-B,而原来ans里加了B,B抵消了,
就相当于没加。
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<set>
#define ll long long
#define N 100005
#define inf 1000000000
using namespace std;
ll read()
{
ll sum=0,f=1;char x=getchar();
while(x<'0'||x>'9'){if(x=='-')f=-1;x=getchar();}
while(x>='0'&&x<='9'){sum=sum*10+x-'0';x=getchar();}
return sum*f;
}
struct hh
{
int id;
ll h;
hh(){}
hh(int a,ll b)
{
h=b;
id=a;
}
friend bool operator <(const hh &a,const hh &b)
{
return (a.h!=b.h) ? (a.h>b.h):(a.id<b.id);
}
};
set<hh> st;
int nex[N],fro[N],n,k;
ll ans=0,a[N];
int main()
{
freopen("so.in","r",stdin);
freopen("so.out","w",stdout);
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
nex[i]=i+1;
fro[i]=i-1;
st.insert(hh(i,a[i]));
}a[0]=-1e15;
nex[n]=0;
while(k--)
{
int x=st.begin()->id;
ans+=a[x];
a[x]=-a[x];
a[x]+=a[fro[x]];
a[x]+=a[nex[x]];
st.erase(st.begin());
st.erase(hh(nex[x],a[nex[x]]));
st.erase(hh(fro[x],a[fro[x]]));
st.insert(hh(x,a[x]));
if(fro[fro[x]])nex[fro[fro[x]]]=x;
if(nex[nex[x]])fro[nex[nex[x]]]=x;
nex[x]=nex[nex[x]];
fro[x]=fro[fro[x]];
}
cout<<ans;
}