题目链接
题目分析
这道题是一道典型的dp问题,在笔者的一番思考之后,发现其实不用dp也可以做这道题(其实是本蒟蒻不会dp) 。
不用dp做这道题的关键就是巧妙的设计一个反悔的机制,使其在选了x之后,发现x的权重(也就是x出现的此处乘以x)比x-1和x+1要小,就可以不选x而选x-1和x+1。
首先我们用一个链表,把所有数都连起来。
然后每一次找出所有数中的最大的数。这个数就是这一次的选择。接下来是最关键的一步,我们将x的权重weight[x]调整为weight[x-1]+weight[x+1]-weight[x],然后删除左右两边的节点,让x左节点变成x-2,右节点变成x+2
这时候,如果后面我们选了x节点,则这两次选取的总分为weight[x]+weight[x+1]+weight[x-1]-weight[x],相当于选取了x+1,x-1节点。
每次找出最大的节点,用大根堆就可以了。
代码
/*haha*/
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <queue>
#include <vector>
#define maxN 100100
using namespace std;
typedef long long LL;
LL arr[maxN];
LL vis[maxN];
LL haha[maxN];
struct node
{
LL val;
LL weight;
LL left;
LL right;
node(){};
bool operator < (const node &a) const{
return weight<a.weight;
}
}ns[maxN];
LL n;
LL ans;
LL MAX;
int main()
{
while(scanf("%lld",&n)!=EOF){
ans=0;
MAX=0;
memset(vis,0,sizeof(vis));
memset(haha,0,sizeof(haha));
priority_queue<node> q;
while(!q.empty()) q.pop();
for(int i=1;i<=n;i++){
scanf("%lld",&arr[i]);
vis[arr[i]]++;
MAX=max(arr[i],MAX);
}
ns[0].right = 1;
ns[MAX+1].left=MAX;
for(int i=1;i<=MAX;i++){
// printf("%lld\n",vis[i]);
ns[i].val=i;
ns[i].weight=vis[i]*i;
ns[i].left=i-1;
ns[i].right=i+1;
if(vis[i]) q.push(ns[i]);
}
while(q.top().weight>0){
while(!q.empty()&&haha[q.top().val]==1) q.pop();
if(q.empty()) break;
node temp = q.top(); q.pop();
if(temp.weight<0) break; //这儿写的有点乱
// printf("%lld\n",temp.weight);
ans+=temp.weight;
LL val=temp.val;
LL left=ns[val].left;
LL right=ns[val].right;
ns[val].weight=ns[left].weight+ns[right].weight-ns[val].weight;
ns[val].left=ns[left].left;
ns[val].right=ns[right].right;
ns[ns[left].left].right=temp.val;
ns[ns[right].right].left=temp.val;
haha[left]=1; haha[right]=1;
q.push(ns[val]);
}
// for(int i=1;i<=MAX;i++){
// printf("%lld ",ns[i].weight);
// }
// printf("\n");
printf("%lld\n",ans);
}
}
其中的haha数组是用来记录删除的节点的,皮了一下嘿嘿嘿。