这种“反悔”操做真的很强php
贪心操做中保证每一步都选取当前最优解,但经过某种转换将一步更改操做转为一个可选取的物品。html
又到了诺德县的百姓孝敬夹克大老爷的日子,带着数量不等的铜板的村民准时汇集到了村口。
夹克老爷是一位很"善良"的老爷,为了体现他的仁慈,有一套特别的收钱的技巧。
一、让全部的村民排成一队,而后首尾相接排成一个圈。
二、选择一位村民收下他的铜钱,而后放过他左右两边的村民。
三、让上述三位村民离开队伍,并让左右两边的其余村民合拢起来继续围成一个圈。
四、重复执行二、3直到村民所有离开。
夹克老爷的家丁早早的组织村民排成一队并清点了村民人数和他们手里的铜钱数量。
做为夹克老爷的首席师爷,你要负责按照夹克老爷的收钱技巧完成纳贡的任务。
聪明的你固然知道夹克老爷并不像他表现出来的那样仁慈,可否收到最多的钱财决定了你是否可以继续坐稳首席师爷的位置。
今年村民的人数是N,恰巧是3的倍数。c++
提示:第2步选择村民时不须要按照任何顺序,你能够选择任何一位仍然在队伍里的村民收取他手中的钱财并放走他两侧的村民(这就意味着你没法同时收取到这两位的铜钱了)git
第一行1个整数N(3 <= N <= 10^5 - 1, N % 3 == 0)
第2 - N + 1行:每行1个数对应村民i手中的铜钱。(0 <= m[i] <= 10^9)spa
一个整数,说明在夹克老爷的收钱规则下你最多可以为夹克老爷搜刮到多少铜钱code
6 6 2 3 4 5 9
13
先来看一个显然很假的作法:将每个人从新赋一个权值,为他本身的钱数减去他相邻两人的钱数和。而后每一次取全部的最大值,而且动态修改这个权值。htm
哈……这个果断假的很……好比n=6,7 9 7 1 2 3就可以卡掉。blog
那么是什么缘由致使这个方法不正确呢?ip
结合上面那个就能够看出,这个新赋的权值表明的只是单个点在周围两个点之间相对的优劣。一个只涉及相邻元素的估价权值,还能期望它起什么做用?……get
可是贪心仍是要贪的。取了元素有后效性,转移又没什么办法转移,这辈子都不可能动态规划的。
那么首先,每次选取的确定要是全局里较优的。可是归根结底来讲,是什么缘由致使了不能每一次都选最大的而后删除其两边元素呢?是由于对于当前最优的操做,过一会有可能发现会有其余方法可以使得之后更优。
看上去这是个贪心思路会遇到的通病,可是路并无被走绝。若是把“反悔”这一步也看作是一个能够选取的物品,那么以后不就会由于“反悔”在全局更优而“改变历史”吗?(固然前提是得到价值的操做与时间无关,而且题目的限制并不会由于反悔历史操做而被违背)
固然“反悔”这个技巧也是要在特定情境下使用的。例如这题,咱们能够发现几个结论:
看上去不就是显然结论吗,可是能够由此推出这个问题的充要条件:选取$n/3$个互不相邻的物品,而且容许将“反悔”做为物品形式选取。
一波酷炫操做以后,换成代码形式就很好理解了……
1 #include<bits/stdc++.h> 2 typedef long long ll; 3 const int maxn = 200035; 4 5 ll n,a[maxn],tot,ans; 6 ll pre[maxn],nxt[maxn]; 7 struct cmp 8 { 9 bool operator () (ll x, ll y) const 10 { 11 return a[x] < a[y]; 12 } 13 }; 14 std::priority_queue<ll, std::vector<ll>, cmp> q; 15 bool fbd[maxn]; 16 17 ll read() 18 { 19 char ch = getchar(); 20 ll num = 0; 21 bool fl = 0; 22 for (; !isdigit(ch); ch = getchar()) 23 if (ch=='-') fl = 1; 24 for (; isdigit(ch); ch = getchar()) 25 num = (num<<1)+(num<<3)+ch-48; 26 if (fl) num = -num; 27 return num; 28 } 29 void deletes(ll id) 30 { 31 ll l = pre[pre[id]], r = nxt[nxt[id]]; 32 a[++tot] = a[pre[id]]+a[nxt[id]]-a[id]; 33 fbd[id] = 1, fbd[pre[id]] = 1, fbd[nxt[id]] = 1; 34 pre[tot] = l, nxt[tot] = r; 35 pre[r] = tot, nxt[l] = tot; 36 q.push(tot); 37 } 38 int main() 39 { 40 n = tot = read(); 41 for (int i=1; i<=n; i++) 42 { 43 pre[i] = i==1?n:i-1, nxt[i] = i==n?1:i+1; 44 a[i] = read(), q.push(i); 45 } 46 for (int i=1; i<=n/3; i++) 47 { 48 int id = q.top(); 49 q.pop(); 50 while (fbd[id]) 51 id = q.top(), q.pop(); 52 deletes(id); 53 ans += a[id]; 54 } 55 printf("%lld\n",ans); 56 return 0; 57 }
Farmer John 有太多的工做要作啊!!!!!!!!为了让农场高效运转,他必须靠他的工做赚钱,每项工做花一个单位时间。 他的工做日从0时刻开始,有1000000000个单位时间(!)。在任一时刻,他均可以选择编号1~N的N(1 <= N <= 100000)项工做中的任意一项工做来完成。 由于他在每一个单位时间里只能作一个工做,而每项工做又有一个截止日期,因此他很难有时间完成全部N个工做,虽然仍是有可能。 对于第i个工做,有一个截止时间D_i(1 <= D_i <= 1000000000),若是他能够完成这个工做,那么他能够获利P_i( 1<=P_i<=1000000000 ). 在给定的工做利润和截止时间下,FJ可以得到的利润最大为多少呢?答案可能会超过32位整型。
第1行:一个整数N. 第2~N+1行:第i+1行有两个用空格分开的整数:D_i和P_i.
输出一行,里面有一个整数,表示最大获利值。
第1个单位时间完成第3个工做(1,7),而后在第2个单位时间完成第1个工做(2,10)以达到最大利润