1. 程式人生 > 其它 >cron和crontab命令詳解 crontab 每分鐘、每小時、每天、每週、每月、每年定時執行 crontab每5分鐘執行一次

cron和crontab命令詳解 crontab 每分鐘、每小時、每天、每週、每月、每年定時執行 crontab每5分鐘執行一次

揹包問題

我們可以從集合的角度來看待這個問題,主要是如何表示狀態以及進行狀態間的轉移(決策)。

通常可以考慮兩個角度

  1. 狀態表示

    1. 集合:當前這個狀態代表的具體的含義
    2. 屬性:它的特點,如最大最小等
  2. 狀態計算

    ​ 狀態計算本質是對集合的劃分,再進一步想其實是對於當前這個集合怎麼劃分才能不重不漏(不一定不重,但一定不能遺漏)以便於後面進行集合的決策轉移,也就是劃分成更小的子集使這些子集都可以被計算出來。

優化的根本是有一個東西代表一類東西,而且大部分是在DAG上做遞推,我當前這個狀態如果從所有能夠到他的狀態都列舉決策過,那麼當前這個狀態一定是最優的(最優子結構),因此對於下一個狀態就不用再從頭開始轉移了,他只會用到一些最優子結構來形成一個新的最優子結構。一般列舉狀態的時候要滿足拓撲序

,保證當前這個狀態進行轉移的時候他的所有的前驅一定都是最優的了,不能有後效性。

01揹包

在一些限制下,每個物品最多選一次。

狀態表示 :f(i,j) :表示從前i個裡面選體積不超過j的最大價值

狀態計算:可以通過第i個物品選於不選將集合劃分成兩個子集,且不重不漏

轉移:$f(i, j) = max(f(i-1,j),f(i-1,j-v[i]+w[i]))$。

發現當前這個狀態是用到了第$i- 1$層的狀態,如果我們想用滾動陣列將$f$優化到一維,我們可以倒敘列舉$j$,因為只會從他前面更小的$j$,因為是倒敘列舉的所以前面更小的$j$對應的還是第$i-1$層的狀態。

轉移:$f(j) = max(f(j),f(j-v[i] + w[i]))$。

完全揹包

在一些限制下每個物品可以選無數次。

狀態表示:f(i,j): 表示從前i個裡面選體積不超過j的最大價值

狀態計算:可以通過將第i個物品選多少個將集合劃分,且補充不漏

對於第i個物品體積不超過j則有:$f(i,j)=max(f(i-1,j),f(i-1,j-v)+w,f(i-1,j-2v)+2w,...,f(i-1,j-kv)+kw)$

對於第i個物品體積不超過j-1則有:$f(i, j-v)= max(\ \ \ \ f(i-1,j-v)+w,f(i-1,j-2v)+2w,...,f(i-1,j-kv)+kw)$

將下面帶入上面的發現$f(i,j)=max(f(i-1,j),f(i, j - v)+w)$

轉移:$f(i,j)=max(f(i-1,j),f(i, j - v)+w)$

發現當前狀態轉移過來用到的是第$i$層的狀態,因此滾動陣列優化只需要正序列舉體積即可。

轉移:$f(j)=max(f(j),f(j-v)+w)$

多重揹包

在一些限制下每個物品可以選一定的次數。

思路一(暴力)

發現我們多加一維來表示第i個選多少個即可,或者假設某個物品可以選k次那就把它拆成k個一樣的物品即可,時間複雜度為$O(nm)$

核心程式碼

for (int i = 1; i <= n; i ++ )
        for (int j = 0; j <= m; j ++ )
            for (int k = 0; k <= s[i] && k * v[i] <= j; k ++ )
                f[i][j] = max(f[i][j], f[i - 1][j - v[i] * k] + w[i] * k);

思路二(二進位制優化)

考慮優化暴力裡的第二個方法,不把k拆成k個1,我們把k轉化成二進位制形式假設為1011,則有$1011=0111+100$,我們可以把它拆成$1,2,4,4$這樣,這樣是可以組合出$0-11$裡的任何一個數的,因為$111$每一位選與不選可以組合出$0-7$中任何一個數,然後對於$100$這個數選與不選相當於讓$0- 7$往後平移$4$,所以可以湊出任何一個數。複雜度降到了$O(mlogn)$

核心轉化程式碼

for (int i = 1; i <= n; i ++ ) {
        int a, b, s;
        cin >> a >> b >> s;
        int k = 1;
        while (k <= s) {
            cnt ++;
            v[cnt] = k * a;
            w[cnt] = k * b;
            s -= k;
            k <<= 1;    
        }
        if (s > 0) {
            cnt ++;
            v[cnt] = s * a;
            w[cnt] = s * b;
        }
    }

思路三 (單調佇列優化)

分組揹包

在一定限制下每組物品最多選一個。

狀態表示:f(i,j):從前i組裡選,且體積是j的最大價值

狀態計算:我們可以多加一維迴圈來列舉第i組裡選哪個,這樣就變成了01揹包

轉移:$f[i][j]=max(f[i][j],f[i-1][j-v[i][k]]+w[i][k]);$

可以像01揹包一樣倒著列舉體積優化掉第一維

轉移:$ f[j] = max(f[j], f[j - v[i][k]] + w[i][k]);$

有依賴的揹包問題

對於一顆樹選擇某個結點就必須選擇他的父節點。

狀態表示:f(i,j):以i為根的子樹必須選根節點,且體積不超過j的最大價值

狀態計算:對於每個結點$u$來說,先遞迴處理它的子節點當子結點資訊正確後再向父節點進行轉移,將集合劃分成當前結點的這個子節點的體積是多少,可以將每一棵子樹看成一個物品組,就變成了一個分組揹包問題。

核心程式碼:

int dfs(int u) {
   for (int i = v[u]; i <= m; i ++ ) f[u][i] = w[u]; // 因為u必選所以給成立的加上一個u的價值
   for (int i = h[u]; ~i; i = ne[i] ) {
       int v = e[i]; dfs(v);
       for (int j = m; j >= v[u]; j -- )
        for (int k = 0; k <= j - v[u]; k ++ ) // 留給選根結點的體積,這樣轉移的過程中任意一個f(i,j)中均含有w[u]這個價值,因為向他轉移的那個f(u,j-k)中一個含有w[u],所以就滿足了依賴性
            f[u][j] = max(f[u][j], f[u][j - k] + f[v][k]); 
   }
}