YBTOJ软件安装&&洛谷P2515(强连通分量)

20 篇文章 1 订阅

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+costuj+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;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值