poj3160——強連通+縮點+樹形dp

題目鏈接:

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章