问题 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; }