现在的位置: 首页 > 综合 > 正文

poj3160——强连通+缩点+树形dp

2013年06月18日 ⁄ 综合 ⁄ 共 2340字 ⁄ 字号 评论关闭

题目链接:

http://poj.org/problem?id=3160

题目大意:

一张有向图,有n个点,m条边,输入“i——j”表示可以从i点走到j点,是单向的。每个点都有权值。

你可以任意选定一个点作为起点,去遍历这个图,当经过一个点的时候,你可以选择取或者不取该点的权值(因为权值有负值)。

求能获得最大权值。

解题思路:

如果是一张有向无环图的话,那么就很简单做树形dp即可。

所以第1步应该是把图化成无环图。

第1步:求强连通分量+缩点,然后建立新图。

第2步:在新图上做树形dp,得到最大的max。

源代码:

#include<stdio.h>
#include<iostream>
#include<math.h>
#include<string.h>
#include<string>
#include<map>
#include<vector>
#include<set>
#include<queue>
#include<algorithm>
using namespace std;
vector<int> child[30001];
vector<int> pre[30001];
int v[30001];           //每个点所拥有的权值
int n,m;
int dp[30001];          //用来记录每个点所能获得的最大权值
struct node
{
    int id;
    int end_time;       //结束时间戳
}s[30001];
bool cmp(node a,node b)     //按照时间戳进行排序
{
    return a.end_time>b.end_time;
}
int vis[30001];
int Set[30001];     //每个点属于的集合
struct node1        //新图的结构体
{
    int v;
    vector<int> child;
}p[30001];
int Time;           //时间戳
int cnt;            //强连通分量的个数
int Max;            //结果的最大值
void dfs1(int now)      //dfs1求取时间戳
{
    int i,j,len,kid;
    len=child[now].size();
    for(i=0;i<len;i++)
    {
        kid=child[now][i];
        if(!vis[kid])
        {
            vis[kid]=1;
            dfs1(kid);
        }
    }
    vis[now]=1;
    s[now].id=now;
    s[now].end_time=++Time;
    return;
}
void dfs2(int now)      //dfs2求取强连通分量
{
    int i,j,len,father;
    len=pre[now].size();
    for(i=0;i<len;i++)
    {
        father=pre[now][i];
        if(!vis[father])
        {
            vis[father]=1;
            dfs2(father);
        }
    }
    Set[now]=cnt;
    if(v[now]>0)
        p[cnt].v+=v[now];       //顺便求取每个强连通点的权值
    return;
}
void dfs3(int now)              //树形dp求取当前节点能获得的最大价值
{
    int i,j,k,len,kid;
    len=p[now].child.size();
    if(len==0)
    {
        dp[now]=p[now].v;
        return;
    }
    for(i=0;i<len;i++)
    {
        kid=p[now].child[i];
        if(!vis[kid])
        {
            vis[kid]=1;
            dfs3(kid);
        }
        dp[now]=max(dp[now],p[now].v+dp[kid]);      //当前节点的最大值等于孩子节点的最大值+本身
    }
    return;
}
int main()
{
    freopen("in.txt","r",stdin);
    int i,j,k,t,len,a,b,id,x,y;
    while(scanf("%d%d",&n,&m)==2)
    {
        memset(v,0,sizeof(v));
        for(i=1;i<=n;i++)   //点默认从1开始
            scanf("%d",&v[i]);
        for(i=0;i<=n;i++)
        {
            child[i].clear();
            pre[i].clear();
        }
        while(m--)
        {
            scanf("%d%d",&a,&b);
            a++;
            b++;
            child[a].push_back(b);
            pre[b].push_back(a);
        }
        //进行dfs1求取时间戳
        memset(vis,0,sizeof(vis));
        Time=0;
        for(i=1;i<=n;i++)
        {
            if(!vis[i])
            {
                vis[i]=1;
                dfs1(i);
            }
        }
        //按照时间戳进行排序
        sort(s+1,s+1+n,cmp);
        //按照排序后的点,进行第dfs2
        memset(vis,0,sizeof(vis));
        memset(p,0,sizeof(p));
        cnt=0;
        for(i=1;i<=n;i++)
        {
            id=s[i].id;
            if(!vis[id])
            {
                cnt++;
                vis[id]=1;
                dfs2(id);
            }
        }
        //建立新图
        for(i=1;i<=n;i++)
        {
            x=Set[i];
            len=child[i].size();
            for(j=0;j<len;j++)
            {
                y=child[i][j];
                y=Set[y];
                if(x==y)    continue;
                p[y].child.push_back(x);        //y是x的孩子节点
                //p[x].du++;                      //统计出度
            }
        }
        //对新图进行树形dp
        Max=0;
        memset(vis,0,sizeof(vis));
        memset(dp,0,sizeof(dp));
        for(i=1;i<=cnt;i++)
        {
            if(!vis[i])
            {
                vis[i]=1;
                dfs3(i);
            }
            Max=max(dp[i],Max);
        }
        printf("%d\n",Max);
    }
    return 0;
}

抱歉!评论已关闭.