您的位置:首页 > 其它

bzoj 2427 HAOI 2010 软件安装 (dp+tarjan缩点)

2017-06-05 09:51 489 查看
题面大概如此:

Description

现在我们的手头有N个软件,对于一个软件i,它要占用Wi的磁盘空间,它的价值为Vi。我们希望从中选择一些软件安装到一台磁盘容量为M计算机上,使得这些软件的价值尽可能大(即Vi的和最大)。

但是现在有个问题:软件之间存在依赖关系,即软件i只有在安装了软件j(包括软件j的直接或间接依赖)的情况下才能正确工作(软件i依赖软件j)。幸运的是,一个软件最多依赖另外一个软件。如果一个软件不能正常工作,那么它能够发挥的作用为0。

我们现在知道了软件之间的依赖关系:软件i依赖软件Di。现在请你设计出一种方案,安装价值尽量大的软件。一个软件只能被安装一次,如果一个软件没有依赖则Di=0,这时只要这个软件安装了,它就能正常工作。

Input

第1行:N, M (0<=N<=100, 0<=M<=500)

第2行:W1, W2, … Wi, …, Wn (0<=Wi<=M )

第3行:V1, V2, …, Vi, …, Vn (0<=Vi<=1000 )

第4行:D1, D2, …, Di, …, Dn (0<=Di<=N, Di≠i )

Output

一个整数,代表最大价值。

Sample Input

3 10

5 5 6

2 3 4

0 1 1

Sample Output

5

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=2427

这道题明显是树上dp,依赖的背包问题。但是题目比较坑的是这个题目允许有环出现,也就是1依赖2,2依赖1则,环上物体要么全选,要么全部不选,所以我们预处理缩点,重新建图,再建一个虚根节点,连接所有入度为0的点,跑一遍dp就可以了。

2427 Accepted 1540 kb 124 ms C++/Edit 3146 B 2017-06-05 09:44:45

AC代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#define N 505
#define INF 0x3f3f3f3f
using namespace std;

struct Edge{
int v, next;
};
Edge e[2 * N], e2[2 * N];//e为最开始的图,e2为重建的图

int num, head
, num2, head2
, in
;

void adde1( int i, int j ) {
e[++num].v = j;
e[num].next = head[i];
head[i] = num;
}
void adde2( int i, int j ) {
in[j] = 1;
e2[++num2].v = j;
e2[num2].next = head2[i];
head2[i] = num2;
}

int inde, dfn
, low
, instack
, stack
, top;//tarjan
int n, m, w
, v
, x, f[105][505];
int scc, sv
, sw
, belong
;
void tarjan( int u ) {//找环
inde++;
dfn[u] = low[u] = inde;
instack[u] = 1;
stack[++top] = u;
for ( int i = head[u]; i; i = e[i].next ) {
int v = e[i].v;
if ( !dfn[v] ) {
tarjan(v);
low[u] = min(low[u], low[v]);
}
else if (instack[v]) low[u] = min(low[u], dfn[v]);
}
if( low[u] == dfn[u] ) {
scc++; int now = 0;
while( now != u ) {
now = stack[top--];
instack[now] = 0;
belong[now] = scc;
sv[scc] += v[now];
sw[scc] += w[now];
}
}
}

void dp( int u ) {//树上dp这里我给出3种写法,大概都差不多(⊙﹏⊙)b
for ( int i = 0; i < sw[u]; i++ ) f[u][i] = 0;
for ( int i = sw[u]; i <= m; i++ ) f[u][i] = sv[u];
for ( int i = head2[u]; i; i = e2[i].next ) {
int v = e2[i].v;
dp(v);
for ( int k = m; k >= sw[u]; k-- )
for ( int j = 0; j <= k-sw[u]; j++ )
f[u][k] = max(f[u][k], f[u][k-j]+f[v][j]);
}
}
/*
void dp( int u ) {
for ( int i = 0; i < sw[u]; i++ ) f[u][i] = 0;
for ( int i = sw[u]; i <= m; i++ ) f[u][i] = sv[u];
for ( int i = head2[u]; i; i = e2[i].next ) {
int v = e2[i].v;
dp(v);
for ( int k = m-sw[u]; k >= 0; k-- )
for ( int j = 0; j <= k; j++ )
f[u][k+sw[u]] = max(f[u][k+sw[u]], f[u][k+sw[u]-j]+f[v][j]);
}
}
*/
/*
void dp( int u ) {
for(int i = head2[u]; i; i = e2[i].next) {
int v = e2[i].v;
dp(v);
for(int j = m - sw[u]; j>= 0; j--) {
for(int k = 0; k <= j; k++)
f[u][j]=max(f[u][j],f[u][k]+f[v][j-k]);
}
}
for(int j = m; j >= 0; j--) {
if(j >= sw[u]) f[u][j] = f[u][j-sw[u]] + sv[u];
else f[u][j] = 0;
}
}
*/
int main() {//主程序都看得懂吧O(∩_∩)O哈!
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", &x );
if ( x != 0 )
adde1( x, i );
}
for ( int i = 1; i <= n; i++ )
if ( !dfn[i] ) tarjan(i);
for ( int i = 1; i <= n; i++ )
for ( int j = head[i]; j; j = e[j].next ) {
int v = e[j].v;
if ( belong[v] != belong[i] )
adde2(belong[i], belong[v]);
}
for ( int i = 1; i <= scc; i++ )
if ( !in[i] ) adde2(scc+1, i);
dp(scc+1);
printf( "%d\n", f[scc+1][m] );
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: