1. 程式人生 > >BZOJ 2427 軟件安裝(強連通分量+樹形背包)

BZOJ 2427 軟件安裝(強連通分量+樹形背包)

oid can blog index return mat 內存 nbsp getch

題意:現在我們的手頭有N個軟件,對於一個軟件i,它要占用Wi的磁盤空間,它的價值為Vi。我們希望從中選擇一些軟件安裝到一臺磁盤容量為M計算機上,使得這些軟件的價值盡可能大(即Vi的和最大)。
但是現在有個問題:軟件之間存在依賴關系,即軟件i只有在安裝了軟件j(包括軟件j的直接或間接依賴)的情況下才能正確工作(軟件i依賴軟件j)。幸運的是,一個軟件最多依賴另外一個軟件。如果一個軟件不能正常工作,那麽它能夠發揮的作用為0

技術分享
# include <cstdio>
# include <cstring>
# include <cstdlib>
# include <iostream>
# include 
<vector> # include <queue> # include <stack> # include <map> # include <bitset> # include <set> # include <cmath> # include <algorithm> using namespace std; # define lowbit(x) ((x)&(-x)) # define pi acos(-1.0) # define eps 1e-8 # define MOD 2007 # define INF
1000000000 # define mem(a,b) memset(a,b,sizeof(a)) # define FOR(i,a,n) for(int i=a; i<=n; ++i) # define FO(i,a,n) for(int i=a; i<n; ++i) # define bug puts("H"); # define lch p<<1,l,mid # define rch p<<1|1,mid+1,r # define mp make_pair # define pb push_back typedef pair<int,int> PII; typedef vector
<int> VI; # pragma comment(linker, "/STACK:1024000000,1024000000") typedef long long LL; int Scan() { int x=0,f=1;char ch=getchar(); while(ch<0||ch>9){if(ch==-)f=-1;ch=getchar();} while(ch>=0&&ch<=9){x=x*10+ch-0;ch=getchar();} return x*f; } const int N=105; //Code begin... struct Edge{int p, next;}edge[N]; int head[N], cnt=1; int W[N], V[N], D[N], cost[N], val[N], dp[N][505], m; int Low[N], DFN[N], Stack[N], Belong[N], dee[N], Index, top, scc; bool Instack[N], vis[N][N]; VI E[N]; void add_edge(int u, int v){edge[cnt].p=v; edge[cnt].next=head[u]; head[u]=cnt++;} void Tarjan(int u){ int v; Low[u]=DFN[u]=++Index; Stack[top++]=u; Instack[u]=true; for (int i=head[u]; i; i=edge[i].next) { v=edge[i].p; if (!DFN[v]) { Tarjan(v); if (Low[u]>Low[v]) Low[u]=Low[v]; } else if (Instack[v]&&Low[u]>DFN[v]) Low[u]=DFN[v]; } if (Low[u]==DFN[u]) { ++scc; do{ v=Stack[--top]; Instack[v]=false; Belong[v]=scc; cost[scc]+=W[v]; val[scc]+=V[v]; }while (v!=u); } } void solve(int n){ mem(DFN,0); mem(Instack,false); Index=scc=top=0; FOR(i,1,n) if (!DFN[i]) Tarjan(i); } void dfs(int x){ FO(i,0,E[x].size()) { int v=E[x][i]; dfs(v); for (int j=m; j>=0; --j) FOR(k,cost[v],j) dp[x][j]=max(dp[x][j],dp[x][j-k]+dp[v][k]); } for (int i=m; i>=cost[x]; --i) dp[x][i]=dp[x][i-cost[x]]+val[x]; } int main () { int n; scanf("%d%d",&n,&m); FOR(i,1,n) scanf("%d",W+i); FOR(i,1,n) scanf("%d",V+i); FOR(i,1,n) { scanf("%d",D+i); if (D[i]) add_edge(D[i],i); } solve(n); FOR(i,1,n) { int u=Belong[i]; for (int j=head[i]; j; j=edge[j].next) { int v=Belong[edge[j].p]; if (u==v||vis[u][v]) continue; E[u].pb(v); vis[u][v]=true; ++dee[v]; } } FOR(i,1,scc) if (!dee[i]) E[0].pb(i); dfs(0); printf("%d\n",dp[0][m]); return 0; }
View Code


我們現在知道了軟件之間的依賴關系:軟件i依賴軟件Di。現在請你設計出一種方案,安裝價值盡量大的軟件。一個軟件只能被安裝一次,如果一個軟件沒有依賴則Di=0,這時只要這個軟件安裝了,它就能正常工作。

依照依賴關系可以建一個圖,這個圖中每個點的入度至多1,不難發現,這是一些環加上樹組成的森林,對於環,要麽不選要麽都選,於是可以把環縮點。

這樣原圖就變成了一個有向森林,對於每個根節點,我們建立一個虛擬節點連向這些節點,於是就變成了一顆樹。

在樹上做樹形依賴背包即可,定義dp[x][v]表示x的子樹占用了v的內存能產生的最大價值。轉移方程很簡單。

時間復雜度O(n^2*m).

BZOJ 2427 軟件安裝(強連通分量+樹形背包)