概述「貪心「反悔」策略」模型

這種「反悔」操做真的很強php

模型概述

貪心操做中保證每一步都選取當前最優解,但經過某種轉換將一步更改操做轉爲一個可選取的物品。html

 

一些例題

51nod1380 夾克老爺的逢三抽一

又到了諾德縣的百姓孝敬夾克大老爺的日子,帶着數量不等的銅板的村民準時彙集到了村口。
夾克老爺是一位很"善良"的老爺,爲了體現他的仁慈,有一套特別的收錢的技巧。
一、讓全部的村民排成一隊,而後首尾相接排成一個圈。
二、選擇一位村民收下他的銅錢,而後放過他左右兩邊的村民。
三、讓上述三位村民離開隊伍,並讓左右兩邊的其餘村民合攏起來繼續圍成一個圈。
四、重複執行二、3直到村民所有離開。
夾克老爺的家丁早早的組織村民排成一隊並清點了村民人數和他們手裏的銅錢數量。
做爲夾克老爺的首席師爺,你要負責按照夾克老爺的收錢技巧完成納貢的任務。
聰明的你固然知道夾克老爺並不像他表現出來的那樣仁慈,可否收到最多的錢財決定了你是否可以繼續坐穩首席師爺的位置。
今年村民的人數是N,恰巧是3的倍數。c++

提示:第2步選擇村民時不須要按照任何順序,你能夠選擇任何一位仍然在隊伍裏的村民收取他手中的錢財並放走他兩側的村民(這就意味着你沒法同時收取到這兩位的銅錢了)git

 

Input

 

第一行1個整數N(3 <= N <= 10^5 - 1, N % 3 == 0)
第2 - N + 1行:每行1個數對應村民i手中的銅錢。(0 <= m[i] <= 10^9)spa

Output

一個整數,說明在夾克老爺的收錢規則下你最多可以爲夾克老爺搜刮到多少銅錢code

Input示例

6
6
2
3
4
5
9

Output示例

13

題目分析

先來看一個顯然很假的作法:將每個人從新賦一個權值,爲他本身的錢數減去他相鄰兩人的錢數和。而後每一次取全部的最大值,而且動態修改這個權值。htm

哈……這個果斷假的很……好比n=6,7 9 7 1 2 3就可以卡掉。blog

那麼是什麼緣由致使這個方法不正確呢?ip

結合上面那個就能夠看出,這個新賦的權值表明的只是單個點在周圍兩個點之間相對的優劣。一個只涉及相鄰元素的估價權值,還能期望它起什麼做用?……get

可是貪心仍是要貪的。取了元素有後效性,轉移又沒什麼辦法轉移,這輩子都不可能動態規劃的

那麼首先,每次選取的確定要是全局裏較優的。可是歸根結底來講,是什麼緣由致使了不能每一次都選最大的而後刪除其兩邊元素呢?是由於對於當前最優的操做,過一會有可能發現會有其餘方法可以使得之後更優。

看上去這是個貪心思路會遇到的通病,可是路並無被走絕。若是把「反悔」這一步也看作是一個能夠選取的物品,那麼以後不就會由於「反悔」在全局更優而「改變歷史」嗎?(固然前提是得到價值的操做與時間無關,而且題目的限制並不會由於反悔歷史操做而被違背)

固然「反悔」這個技巧也是要在特定情境下使用的。例如這題,咱們能夠發現幾個結論:

  • 一共選取$n/3$個物品
  • 選取的物品互不相鄰而且去除相鄰物品後仍然不相鄰
  • 必定有兩個相鄰的物品之間相隔大於一個不被選取的物品

看上去不就是顯然結論嗎,可是能夠由此推出這個問題的充要條件:選取$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 }

 

bzoj1572: [Usaco2009 Open]工做安排Job

Description

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位整型。

Input

第1行:一個整數N. 第2~N+1行:第i+1行有兩個用空格分開的整數:D_i和P_i.

Output

輸出一行,裏面有一個整數,表示最大獲利值。

Sample Input

3
2 10
1 5
1 7

Sample Output

17

HINT 

第1個單位時間完成第3個工做(1,7),而後在第2個單位時間完成第1個工做(2,10)以達到最大利潤


題目分析

其實還有1029: [JSOI2007]建築搶修這題也是同樣,運用題目性質和堆實現了「反悔」操做。
 
 
END
相關文章
相關標籤/搜索