YBTOJ软件安装&&洛谷P2515(强连通分量)
题目传送门
思路:
强连通分量+树形DP
首先对于每个i,从Di向i建一条有向边。
在这里我们发现,依赖关系可以形成环。对于一个环,里面的节点要么都选,要么都不选。所以,这里先tarjan强连通分量缩点,构成一个新图,这样新图里的每一个节点可以看成一个整体考虑(因为对应的原图里的节点要么选要么都不选)。然后新建一个虚拟节点,向新图里所有的入度为0的节点建一条有向边,构成一棵树,以虚拟节点作为根。
建树完毕后,在构成的树上做DP。
以下cost[i]和val[i]分别为树上每个节点的费用和价值,设f[u][i]为在节点u的子树内,费用限制为i的条件下能取到的最大价值,此时对于,每个u,首先把f[u][i](cost[u]<=i<=m)设为val[u]。
然后如果第一层循环u的子节点v,第二层倒序循环i(从m-cost[u]到0),第三层顺序循环节点u的子树的费用限制j(从0到i),则转移方程为:
f
u
,
i
+
c
o
s
t
u
=
max
(
f
u
,
i
+
c
o
s
t
u
,
f
u
,
i
+
c
o
s
t
u
−
j
+
f
v
,
j
)
f_{u,i+cost_u} = \max (f_{u,i+cost_u},f_{u,i+cost_u-j}+f_{v,j})
fu,i+costu=max(fu,i+costu,fu,i+costu−j+fv,j);
最后答案为f[虚拟节点][m]。
以上内容摘自《信息学奥赛一本通高效进阶指南》,纯属水长度
思路应该还是很明显的树形dp
但是有亿些细节卡了我挺长时间
刚开始我dp的时候从1号点开始,卡了一会
然后又因为缩点之后忘记连0号点了……
又卡了挺长时间……
AC代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn=105;
int n,m,w[maxn],v[maxn],tmp,ecnt,bcnt,head[maxn][2],dfn[maxn],cnt,low[maxn];
int st[maxn],top,c[maxn],cost[maxn],val[maxn],f[maxn][505],vis[maxn],out[maxn];
struct edge
{
int from,to,nxt;
}e[505],b[505];
void add1(int x,int y)
{
// printf("u:%d v:%d\n",x,y);
b[++bcnt]=(edge){x,y,head[x][0]};
head[x][0]=bcnt;
}
void add2(int x,int y)
{
// printf("u:%d v:%d\n",x,y);
e[++ecnt]=(edge){x,y,head[x][1]};
head[x][1]=ecnt;
}
void tarjan(int x)
{
dfn[x]=low[x]=++cnt;vis[x]=1;
st[++top]=x;
for(int i=head[x][0];i;i=b[i].nxt)
{
int y=b[i].to;
if(!dfn[y])
{
tarjan(y);
low[x]=min(low[x],low[y]);
}
else if(vis[y]) low[x]=min(low[x],dfn[y]);
}
if(dfn[x]==low[x])
{
tmp++;
do
{
c[st[top]]=tmp;
cost[tmp]+=w[st[top]];
val[tmp]+=v[st[top]];
vis[st[top]]=0;
top--;
}while(x!=st[top+1]);
}
}
void dp(int u)
{
for(int i=cost[u];i<=m;i++) f[u][i]=val[u];
for(int k=head[u][1];k;k=e[k].nxt)
{
int v=e[k].to;
dp(v);
for(int i=m-cost[u];i>=0;i--)
for(int j=0;j<=i;j++)
f[u][i+cost[u]]=max(f[u][i+cost[u]],f[u][i+cost[u]-j]+f[v][j]);
}
}
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++)
{
int x;scanf("%d",&x);
add1(x,i);
}
for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i);
for(int i=1;i<=bcnt;i++)
{
int u=b[i].from,v=b[i].to;
if(c[u]!=c[v]) add2(c[u],c[v]),out[c[v]]=1;
}
for(int i=1;i<=n;i++) if(!out[i]) add2(0,i);
// for(int i=1;i<=tmp;i++) cout<<val[i]<<" ";
// cout<<endl;
for(int i=0;i<=tmp;i++) dp(i);
// for(int i=0;i<=n;i++)
// {
// for(int j=0;j<=m;j++) cout<<f[i][j]<<" ";
// cout<<endl;
// }
printf("%d",f[0][m]);
return 0;
}