POJ3160 Father Christmas flymouse 强连通tarjan算法+dfs+简单dp


题意:flymouse要去送礼物,送的每个人有一定的反馈comfort值,有正有负,给一个有向图,
要你找出一条路径,沿着这个路径去送礼物可以使总的comfort值最大。
注意题意里面的一个关键点:flymouse经过每个房间时,可以选择进去或者不进去,所以我们可以把负值的comfort值赋为0。

思路:
先用tarjan算法处理一遍强连通分量。
然后缩点成有向无环图。
然后简单dp:对有向无环图的每个节点进行有返回值的dfs,对树节点dfs递归时,比较子节点的返回值,取较大的作为返回值。

思路正确但还是wa了很多次,原因出在dfs上,还是太菜了。

#include<iostream>
#define min(a,b) (a<b?a:b)
using namespace std;
const int N=30005,M=150005;
int n,m;
int cft[N];// comfort
struct Edge
{
	int u,v,next;
}edge[2*M];
int edgehead[N];
int k;
int dfn[N],low[N];
int index;
int stack[N];
bool instack[N];
int sp;
int father[N];
int head[N];
int pth;
struct
{
	int u,v;
}add[M];
int addn;
void addedge(int u,int v)
{
	edge[k].u=u;
	edge[k].v=v;
	edge[k].next=edgehead[u];
	edgehead[u]=k++;
}
void tarjan(int now)
{
	dfn[now]=low[now]=++index;
	stack[sp++]=now;
	instack[now]=true;
	for(int i=edgehead[now];i;i=edge[i].next)
	{
		int v=edge[i].v;
		if(!dfn[v])
		{
			tarjan(v);
			low[now]=min(low[now],low[v]);
		}
		else if(instack[v])
		{
			low[now]=min(low[now],dfn[v]);
		}
	}
	if(low[now]==dfn[now])
	{
		int j;
		head[pth++]=now;
		do
		{
			j=stack[--sp];
			instack[j]=false;
			father[j]=now;
			if(j!=now)
				cft[now]+=cft[j];
		}while(j!=now);
	}
}
bool vis[N];
int dfs(int now)
{
	int ret=0;
	int pre=0;
	for(int i=edgehead[now];i;i=edge[i].next)
	{
		int v=father[edge[i].v];
		int u=now;
		if(u!=v)//&&!vis[v])
		{
			//vis[v]=true;
			int tmp=dfs(v);
			if(tmp>ret)
			{
				ret=tmp;
			}
			//else
			//{
			//	vis[v]=false;
			//}
		}
	}
	return ret+cft[now];
}
void solve()
{
	for(int i=1;i<=n;i++)
	{
		if(!dfn[i])
		{
			tarjan(i);
		}
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=edgehead[i];j;j=edge[j].next)
		{
			int u=i;
			int v=edge[j].v;
			if(father[u]!=father[v])
			{
				//addedge(father[u],father[v]);
				add[addn].u=father[u];
				add[addn++].v=father[v];
			}
		}
	}
	for(int i=1;i<addn;i++)
		addedge(add[i].u,add[i].v);
	int ans=0;
	for(int i=1;i<=pth-1;i++)
	{
		//memset(vis,0,sizeof(vis));
		//vis[head[i]]=true;
		int ret=dfs(head[i]);
		if(ret>ans)
			ans=ret;
	}
	printf("%d\n",ans);
}
int main()
{
	while(scanf("%d%d",&n,&m)!=EOF)
	{
		k=1;
		index=1;
		sp=1;
		pth=1;
		addn=1;
		memset(add,0,sizeof(add));
		memset(cft,0,sizeof(cft));
		memset(vis,0,sizeof(vis));
		memset(head,0,sizeof(head));
		memset(father,0,sizeof(father));
		memset(instack,0,sizeof(instack));
		memset(stack,0,sizeof(stack));
		memset(dfn,0,sizeof(dfn));
		memset(low,0,sizeof(low));
		memset(edge,0,sizeof(edge));
		memset(edgehead,0,sizeof(edgehead));
		for(int i=1;i<=n;i++)
		{
			scanf("%d",&cft[i]);
			if(cft[i]<0)
				cft[i]=0;
		}
		int from,to;
		for(int i=1;i<=m;i++)
		{
			scanf("%d%d",&from,&to);
			addedge(from+1,to+1);
		}
		solve();
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值