【HAOI 2010】軟體安裝
阿新 • • 發佈:2018-12-16
題目描述
現在我們的手頭有 個軟體,對於一個軟體 ,它要佔用 的磁碟空間,它的價值為 。我們希望從中選擇一些軟體安裝到一臺磁碟容量為 計算機上,使得這些軟體的價值儘可能大(即 的和最大)。
但是現在有個問題:軟體之間存在依賴關係,即軟體i只有在安裝了軟體 (包括軟體 的直接或間接依賴)的情況下才能正確工作(軟體 依賴軟體 )。幸運的是,一個軟體最多依賴另外一個軟體。如果一個軟體不能正常工作,那麼它能夠發揮的作用為 。
我們現在知道了軟體之間的依賴關係:軟體 依賴軟體 。現在請你設計出一種方案,安裝價值儘量大的軟體。一個軟體只能被安裝一次,如果一個軟體沒有依賴則 ,這時只要這個軟體安裝了,它就能正常工作。
,,,,,。
演算法分析
與金明的預算方案不同的是,這裡的依賴關係可能會形成一個環,跑一邊 Tarjan 演算法縮環,如果我們選擇了這個環中的一個節點,那麼為了獲得價值,該環內的每個點都必選,因此可以看成一個整體,縮環後整個圖變成了一棵樹,計算樹上有依賴揹包 DP 即可。
其實可以像 【JSOI 2016】最佳團體 那樣進一步縮小列舉範圍,但是這題不卡常,就懶得寫了。
第一次寫的時候居然先清空圖再跑了 Tarjan,in[]
陣列的必要性在於新圖的根節點處可能是一個環。
程式碼實現
#include <cstdio>
#include <cstring>
#include <algorithm>
const int maxn=105;
const int maxm=505;
int head[maxn],ev[maxn],nxt[maxn],idx=0;
inline void add(int u,int v) {ev[++idx]=v;nxt[idx]=head[u];head[u]=idx;}
int dfn[maxn], low[maxn],dfnidx=0,sta[maxn],top=0;
int sccno[maxn],sccidx=0;
void tarjan(int x) {
dfn[x]=low[x]=++dfnidx;sta[top++]=x;
for(int i=head[x];i;i=nxt[i]) {
int v=ev[i];
if(!dfn[v]) {
tarjan(v);
low[x]=std::min(low[x],low[v]);
}
else if(!sccno[v]) low[x]=std::min(low[x],dfn[v]);
}
if(dfn[x]==low[x]) {
++sccidx;
while(top) {
int u=sta[--top];
sccno[u]=sccidx;
if(x==u) break;
}
}
}
int n,m,W[maxn],V[maxn],d[maxn];
int w[maxn],v[maxn],in[maxn],f[maxn][maxm];
void dfs(int x) {
for(int i=0;i<=m;++i) f[x][i]=-0x3f3f3f3f;
f[x][0]=0;if(w[x]<=m) f[x][w[x]]=v[x];
for(int i=head[x];i;i=nxt[i]) {
int v=ev[i];dfs(v);
for(int j=m;j>=0;--j) {
for(int k=0;k<=j-w[x];++k) {
f[x][j]=std::max(f[x][j],f[x][j-k]+f[v][k]);
}
}
}
}
int main() {
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i) scanf("%d",&W[i]);
for(int i=1;i<=n;++i) scanf("%d",&V[i]);
for(int i=1;i<=n;++i) {
scanf("%d",&d[i]);
if(d[i]) add(d[i],i);
}
for(int i=1;i<=n;++i) if(!dfn[i]) tarjan(i);
memset(head,0,sizeof(head));idx=0;sccno[0]=0;
for(int i=1;i<=n;++i) {
w[sccno[i]]+=W[i];v[sccno[i]]+=V[i];
if(d[i]&&sccno[d[i]]!=sccno[i]) {
add(sccno[d[i]],sccno[i]);
++in[sccno[i]];
}
}
for(int i=1;i<=sccidx;++i) if(!in[i]) add(0,i);
dfs(0);
int ans=-0x3f3f3f3f;
for(int i=0;i<=m;++i) ans=std::max(ans,f[0][i]);
printf("%d\n",ans);
return 0;
}