生活随笔
收集整理的這篇文章主要介紹了
[POI 2009] gas 贪心
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
Pro
??GAS gastask
? 給出一個n個節點的樹,現在要在這棵樹上放置一些指示物:
? 1.?????? 一個節點可以放置多個指示物;
? 2.?????? 一個指示物擁有一個籠罩范圍,即與它本身所在節點的距離在k個節點之內的任何節點,它都可以選擇籠罩
? 3.?????? 每個指示物最多籠罩s個不同的節點。
? 問題是使用最少的指示物將整棵樹全部籠罩到。
Solution
題目給出了一棵樹,可以比較容易地得到對于一個節點,我們找到一個從它這里可以覆蓋到的最遠的子節點,那么從它到這個子節點的路徑上的每一個節點設立指示物都可以覆蓋到這個子節點,那么從當前的這個節點設立應該是最優的,這樣有沒有可能有反例呢?
我們假設這條路徑中有某一個節點i的子樹中有另一個節點j,這個節點是i可以到達而之前的那個祖先節點所無法到達的,那么這個時候就有可能出現在節點i設立指示物比在祖先節點設立更優的情況(想想就知道),這就出現了反例。
為了避免這種情況,我們可以每次找深度最大的一個葉子節點,然后再找離它最遠的祖先節點作為覆蓋它的節點,那么就可以保證這樣的決策不會出現上面的反例。
由于最深的節點滿足拓撲序,所以可以使用一個隊列來維護,對于每一個節點查詢覆蓋節點。整個算法的復雜度為O(nk)。
不過這樣實現起來比較麻煩,這里我學到了一個來自濤哥的方法:我們使用f[i][j]表示從i號節點向子樹中深搜j步所能到達的節點個數,g[i][j]表示從i號向子樹中深搜j步所能到達的節點現在設立的指示物還能覆蓋多少節點(有點繞)。
我們先深度遍歷每一個點,然后遞歸處理出每個點能覆蓋它的子樹的節點個數最大是多少,由于這樣是最優的所以我們在這個節點設立相應的指示物來覆蓋它們,同時更新還能覆蓋的節點個數,然后我們還要更新覆蓋點和被覆蓋點都在當前節點子樹中的情況,即跨立的情況,如果子樹中有某一個節點可以到達子樹中的另外一個點并且其當先設立的指示物還能繼續覆蓋點,那么就把這些點覆蓋。這樣深搜完之后,再在外面做一遍非最優的決策看還有什么沒有被調整到的。
#include <stdio.h>
#include <memory.h>const int nmax = 100000, kmax = 20;int n, k, s, f[nmax + 18][kmax + 18], g[nmax + 18][kmax + 18];
int fst[nmax + 18], pnt[(nmax << 1)+ 18], nxt[(nmax << 1)+ 18], tot = 0, ans = 0;
bool ed[nmax + 18];void addedge (int s, int t)
{pnt[++tot] = t;nxt[tot] = fst[s];fst[s] = tot;pnt[++tot] = s;nxt[tot] = fst[t];fst[t] = tot;
}int top (int x)
{return x ? (x - 1) / s + 1 : 0;
}void adjust (int &a, int &b)
{if (a >= b) a -= b, b = 0;else b -= a, a = 0;
}void dfs (int i)
{ed[i] = true;for (int kk = fst[i], j = pnt[kk]; kk; kk = nxt[kk], j = pnt[kk])if (!ed[j]){dfs (j);for (int l = 1; l <= k; ++l){f[i][l] += f[j][l - 1];g[i][l] += g[j][l - 1];g[i][l] <?= n;}}ans += top (f[i][k]);f[i][0] = 1;g[i][0] = top (f[i][k]) * s;for (int j = k; j >= 0; --j){adjust (f[i][k - j], g[i][j]);if (j < k) adjust (f[i][k - j - 1], g[i][j]);}
}int main ()
{freopen ("gas.in", "r", stdin);freopen ("gas.out", "w", stdout);scanf ("%d%d%d", &n, &s, &k);for (int i = 1, s, t; i < n; ++i)scanf ("%d%d", &s, &t), addedge (s, t);dfs (1);for (int i = k; i >= 0; --i)for (int j = k - i; j >= 0; --j)adjust (f[1][j], g[1][i]);int S = 0;for (int i = 0; i <= k; ++i)S += f[1][i];printf ("%d", ans + top (S));return 0;
}
轉載于:https://www.cnblogs.com/Neroysq/archive/2011/08/19/2145412.html
總結
以上是生活随笔為你收集整理的[POI 2009] gas 贪心的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。