原题:
现在我们的手头有N个软件,对于一个软件i,它要占用Wi的磁盘空间,它的价值为Vi。我们希望从中选择一些软件安装到一台磁盘容量为M计算机上,使得这些软件的价值尽可能大(即Vi的和最大)。
但是现在有个问题:软件之间存在依赖关系,即软件i只有在安装了软件j(包括软件j的直接或间接依赖)的情况下才能正确工作(软件i依赖软件j)。幸运的是,一个软件最多依赖另外一个软件。如果一个软件不能正常工作,那么它能够发挥的作用为0。
我们现在知道了软件之间的依赖关系:软件i依赖软件Di。现在请你设计出一种方案,安装价值尽量大的软件。一个软件只能被安装一次,如果一个软件没有依赖则Di=0,这时只要这个软件安装了,它就能正常工作。
n<=100,m<=500,wi<=m,vi<=1000
解法很简单,注意到软件可能相互依赖,所以tarjan缩环然后树上dp
这题我卡了一晚上,总是90分
然后翻博客
两年前我是这样写的:照着AC代码瞎改一通,过了但是没想明白为什么
两年后我又做了同样的事情
对比两份代码发现
写法一:每个节点开始只有f[w[i]]=v[i]其余为-oo,然后f[father[x]][i]=max(f[father[x]][i],f[father[x]][i-j]+f[x][j])
写法二:开始所有f[i][j]为0,然后f[father[x]][i]=max(f[father[x]][i],f[father[x]][i-j]+f[x][j]),最后
for(int i=m;i>=w[x];--i) f[x][i]=f[x][i-w[x]]+v[x];
for(int i=0;i<w[x];++i) f[x][i]=0;
写法一90写法二100
继续研究发现,把写法一从f[i][w[i]]=v[i]改成f[i][j]=v[i](w[i]<=j<=m)就过了
要到数据,把两种初始化方法打表出来观察
终于发现!
缩点之后点的质量会大于包容量
然后越界了
代码:(因为一开始没有意识到要用拓扑排序或重新建图所以写得有点屎):
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstdio> 5 #include<algorithm> 6 using namespace std; 7 int rd(){int z=0,mk=1; char ch=getchar(); 8 while(ch<'0'||ch>'9'){if(ch=='-')mk=-1; ch=getchar();} 9 while(ch>='0'&&ch<='9'){z=(z<<3)+(z<<1)+ch-'0'; ch=getchar();} 10 return z*mk; 11 } 12 struct edg{int y,nxt;}e[210]; int lk[110],ltp=0; 13 void ist(int x,int y){ 14 e[++ltp]=(edg){y,lk[x]}; lk[x]=ltp; 15 //e[++ltp]=(edg){x,lk[y]}; lk[y]=ltp; 16 } 17 int n,m,a[110],b[110]; int fth[110]; 18 int dfn[110],low[110],dft=0; 19 int q[110],hd=0; 20 int cid[110],ctp=0; 21 int c[110],d[110]; 22 bool g[110][110]; 23 int f[110][51000]; //!!! 24 bool vstd[110]; 25 int nd[110]; 26 int tth[110]; 27 void tj(int x,int y){ 28 dfn[x]=++dft; low[x]=dfn[x]; 29 q[++hd]=x; 30 for(int i=lk[x];i;i=e[i].nxt){//if(e[i].y!=y){ 31 if(!dfn[e[i].y]){ 32 tj(e[i].y,x); 33 low[x]=min(low[x],low[e[i].y]); 34 } 35 else if(!cid[e[i].y]) 36 low[x]=min(low[x],low[e[i].y]); 37 } 38 if(dfn[x]==low[x]){ 39 ++ctp; 40 int tmp=0; 41 for(;tmp!=x && hd;tmp=q[hd--]){ 42 cid[q[hd]]=ctp; 43 c[ctp]+=a[q[hd]]; 44 d[ctp]+=b[q[hd]]; 45 } 46 } 47 } 48 void dfs(int x,int y){ 49 vstd[x]=true; 50 f[x][c[x]]=d[x]; 51 for(int i=lk[x];i;i=e[i].nxt)if(e[i].y!=y) 52 dfs(e[i].y,x); 53 for(int j=m;j>=0;--j)for(int k=0;j-k>=0;++k) 54 f[y][j]=max(f[y][j],f[y][j-k]+f[x][k]); 55 } 56 void prvs(){ 57 ltp=0; 58 for(int i=1;i<=n;++i) lk[i]=0; 59 for(int i=1;i<=n;++i) dfn[i]=0; 60 hd=0; ctp=0; 61 for(int i=1;i<=n;++i) c[i]=0,d[i]=0; 62 for(int i=1;i<=n;++i)for(int j=1;j<=n;++j) g[i][j]=false; 63 for(int i=0;i<=n;++i)for(int j=0;j<=m;++j) f[i][j]=-1000000007; 64 //注意背包初值 65 for(int i=1;i<=n;++i) vstd[i]=false; 66 for(int i=1;i<=n;++i) nd[i]=0; 67 } 68 int main(){ 69 //freopen("ddd.in","r",stdin); 70 cin>>n>>m; prvs(); 71 for(int i=1;i<=n;++i) a[i]=rd(); 72 for(int i=1;i<=n;++i) b[i]=rd(); 73 for(int i=1;i<=n;++i){ 74 fth[i]=rd(); 75 if(fth[i]) ist(i,fth[i]); 76 } 77 for(int i=1;i<=n;++i)if(!dfn[i]) 78 tj(i,0); 79 for(int i=1;i<=n;++i)if(cid[i]!=cid[fth[i]]) tth[cid[i]]=cid[fth[i]]; 80 n=ctp; 81 for(int i=1;i<=n;++i) fth[i]=tth[i]; 82 for(int i=1;i<=n;++i)if(fth[i]) ++nd[fth[i]]; //注意是有根树,只能用拓扑排序dp 83 hd=0; 84 for(int i=1;i<=n;++i)if(!nd[i]) q[++hd]=i; 85 f[0][0]=0; //注意0节点初值 86 for(int i=1;i<=n;++i) f[i][c[i]]=d[i]; 87 //注意初始化的位置,不能在下面 88 for(int k=1;k<=hd;++k){ 89 //f[q[k]][c[q[k]]]=d[q[k]]; 90 for(int i=m;i>=0;--i)for(int j=0;i-j>=0;++j) 91 f[fth[q[k]]][i]=max(f[fth[q[k]]][i],f[fth[q[k]]][i-j]+f[q[k]][j]); 92 --nd[fth[q[k]]]; 93 if(!nd[fth[q[k]]]) q[++hd]=fth[q[k]]; 94 } 95 int ans=0; 96 for(int i=0;i<=m;++i) ans=max(ans,f[0][i]); 97 cout<<ans<<endl; 98 return 0; 99 }