POJ3160 贪心+tarjan缩点+记忆化搜索

POJ3160
题意:

有向图N<=30000,M<=150000,每个点有点权

每个点跟路径都能经过多次,到达每个点后可选择进屋,若进屋就获得该点的点权

问能获得的最大点权

思路:

直接跑记忆化,有环,会T,加个vis也不行,因为每个点能经过多次

考虑贪心,在一个强连通分量里,能加分的就尽量加分

说到强连通我们就会想到tarjan

那么我们缩点得到DAG之后跑记忆化就行了

DPi表示从i点集出发能获得的最大值

79ms

#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<string.h> 
#include<vector>
using namespace std;
const int maxn=30005;
const int maxe=150005;
int low[maxn];
int dfn[maxn];
bool ins[maxn];
int sk[maxn];
int poi=0,idx=0,cnt=0;
int num=0;
int head[maxn];
int to[maxe*2];
int nxt[maxe*2];

int dp[maxn];
int val[maxn];
vector<int>dag[maxn];
int scc[maxn];
int scv[maxn];
void add(int u,int v)
{
	cnt++;nxt[cnt]=head[u];
	head[u]=cnt; to[cnt]=v;
}
void tarjan(int u)
{
	dfn[u]=low[u]=++idx;
	ins[u]=1;
	sk[poi++]=u;
	for(int i=head[u];i!=-1;i=nxt[i])
	{
		int v=to[i];
		if(!dfn[v])
		{
			tarjan(v);
			low[u]=min(low[u],low[v]);
		}
		else if(ins[v]) low[u]=min(low[u],dfn[v]);
	}
	if(low[u]==dfn[u])
	{
		num++;
		int t;
		do{
			t=sk[--poi];
			scc[t]=num;
			ins[t]=0;
			scv[num]+=max(0,val[t]);
		}
		while(t!=u);
	}
}
int dfs(int u)
{
    if(dp[u]!=0)return dp[u];
    for(int i=0;i<dag[u].size();i++)
    {
        int v=dag[u][i];
        dp[u]=max(dp[u],dfs(v)); 
    } 
    dp[u]+=scv[u];
    return dp[u];
}
int main()
{
	int n,m;
	while(~scanf("%d %d",&n,&m))
	{
		int u,v;
		memset(ins,0,sizeof(ins));
		memset(dfn,0,sizeof(dfn));
		memset(low,0,sizeof(low));
		memset(scv,0,sizeof(scv));
		memset(dp,0,sizeof(dp));
		memset(head,-1,sizeof(head));
		num=poi=cnt=idx=0;
		for(int i=1;i<=n;i++)
            scanf("%d",&val[i]);
		for(int i=1;i<=m;i++)
		{
			scanf("%d %d",&u,&v);
			u++,v++;
			add(u,v);
		}
		for(int i=1;i<=n;i++)
		{
            dag[i].clear();
			if(!dfn[i])
				tarjan(i);
		}
		for(int u=1;u<=n;u++)
		{
			for(int i=head[u];i!=-1;i=nxt[i])
			{
				int v=to[i];
				if(scc[u]!=scc[v])
				{
					dag[scc[u]].push_back(scc[v]);
				}
			}
		} 
		int ans=0;
        for(int i=1;i<=num;i++)
        {
            if(dp[i]==0)
                ans=max(ans,dfs(i));
        }  
        printf("%d\n",ans);     
	}	
} 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值