【贪心+替罪羊】【codevs 1342】种树

一个环形广场周围有 n 个空位,第 i 个空位有一个美观度 ai

现在想把 m 颗树种进这些空位中,每个种了树的位置都会为广场提供其相 应的美观度
由于某些规划问题,相邻两个空位中不能同时种上树,试求可能取得的最大 美观度
【输入格式】
第一行包含两个正整数 n 和 m
第二行包含 n 个正整数 a1~an
【输出格式】
一行一个整数,表示可能取得的最大美观度
【输入样例】
7 3 1 2 3 4 5 6 7
【输出样例】
15
【数据范围】
1≤2m+1≤n≤1.5×105
1≤ai≤103

如果直接按照美观度贪心的话肯定是不正确的

考虑相邻的三个元素a,b,c,如果b>a,b>c,a+c>b

明显应该选择a+c而不是b

为了解决这种情况,我们引入了一个叫替罪羊的概念,使得不改变贪心算法的大方向,而能够自动弥补错误

每次种树选取美观度最大的b,删掉自身及位置相邻的a,b,c,然后push一个新元素(替罪羊)a+c-b

如果b和替罪羊都被删掉则说明选择了a+c

如果只有b被删掉了则说明选择了b

 1 #include <cstdio>
 2 int n,m,ans=0,sz=0,heap[200001],nxt[200001],bak[200001],a[200001];
 3 void keep(int x)
 4 {
 5     int l=x*2,r=l+1,w,t;
 6     if (l<=sz&&a[heap[l]]>a[heap[x]]) w=l;else w=x;
 7     if (r<=sz&&a[heap[r]]>a[heap[w]]) w=r;
 8     if (w!=x)
 9     {
10         t=heap[x];heap[x]=heap[w];heap[w]=t;
11         keep(w);
12     }
13 }
14 void push(int x)
15 {
16     heap[++sz]=x;
17     for (int i=sz/2;i>=1;i/=2) keep(i);
18 }
19 void pop()
20 {
21     heap[1]=heap[sz--];
22     keep(1);
23 }
24 int main()
25 {
26     scanf("%d%d",&n,&m);
27     if (n<m*2)
28     {
29         printf("Error!");
30         return 0;
31     } 
32     nxt[1]=2;nxt[n]=1;bak[1]=n;bak[n]=n-1;
33     for (int i=2;i<n;i++) nxt[i]=i+1,bak[i]=i-1;
34     for (int i=1;i<=n;i++) scanf("%d",&a[i]);
35     for (int i=1;i<=n;i++) push(i);
36     for (int i=1;i<=m;i++)
37     {
38         int m,l,r;
39         m=heap[1],l=nxt[m],r=bak[m];
40         while(bak[nxt[m]]!=m)
41         {
42             pop();
43             m=heap[1],l=nxt[m],r=bak[m];
44         }
45         bak[nxt[l]]=m;nxt[m]=nxt[l];
46         nxt[bak[r]]=m;bak[m]=bak[r];
47         ans+=a[m];
48         a[m]=a[l]+a[r]-a[m];//替罪羊a+c-b 
49         keep(1);
50     }
51     printf("%d",ans); 
52 }

 

转载于:https://www.cnblogs.com/algonote/p/7271678.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值