立志成为饮水机!

洛谷 P3387 【模板】缩点 DAGdp学习记

我们以洛谷P3387 【模板】缩点 来学习DAGdp

1.这道题的流程

//伪代码
  for i->n if(i未被遍历) tarjan(i)
  缩点()
  DAGdp()
  完成 

首先tarjan这部分应该没问题,如果想看详细的可以看我的tarjan学习记

接下来tarjan完毕,每个点属于的强连通分量也得到了,因此缩点可以进行了

这里这部分比较麻烦,下面上的代码讲的比较清楚,注释也给了。

所以现在讲讲DAGdp

我刚开始看到DAPdp……什么鬼啊?(UPD:DAG为有向无环图),然后百度,啥都没有,于是自己用类似用类似拓扑排序的方法做,发现DAGdp就是在拓扑上面弄得,那么,这就好办了

void dagdp()
{
    int i,j;
    queue <int> q;   
    for(i=1;i<=cnt;i++)
    {
        if(!ind[i])  //找到入度为0的点,这个点一定不会被刷新,因此满足dp无后效性 
        {
        q.push(i);
        f[i]=money[i];    
        } 
    }
    while(!q.empty())
    {
        int t=q.front();
        int i,j,k;
        q.pop();
        for(i=head[t];i;i=e[i].next)
        {
        j=e[i].to;
        ind[j]--;  //这个点的入度减一 
        k=money[j];
        f[j]=max(f[t]+k,f[j]);   
        if(!ind[j])  //如果这个点入度为0,那么这个点一定被处理完了 
        q.push(j);  //那么又可以从这个点开始做 
        }
    }
}

因此,这道题的程序就长这个样子

#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<queue>
#include<stack>

using namespace std;

int size,n,m,dt,cnt;
int head[10020],cost[10020],vis[10020],bd[10020],ins[10020],dfn[10020],low[10020],money[10010];
stack <int> s;
int ind[10020],chd[10020],f[10020],ans=-19000017,bl;  
struct edge{
    int next,to,dis;
}e[1000860],looker[1000860]; //looker是存的边的备份 

void addedge(int next,int to,int dis)
{
    e[++size].dis=dis;
    e[size].to=to;
    e[size].next=head[next];
    head[next]=size;
}

int pd(int a,int b)  //判断边是否重复 
{
    for(int i=head[a];i;i=e[i].next)
    {
        int j=e[i].to;
        if(j==b) return 1;
    }
    return 0;
}

void tarjan(int t)  //tarjan操作 
{
    dfn[t]=low[t]=++bl;
    s.push(t);
    ins[t]=1;
    int i,j;
    for(i=head[t];i;i=e[i].next)
    {
        j=e[i].to;
        if(!dfn[j])
        {
            tarjan(j);
            low[t]=min(low[t],low[j]);
        }
        else if(ins[j]) low[t]=min(dfn[j],low[t]);
    }
    j=0;
    if(dfn[t]==low[t])
    {
        cnt++;
        while(t!=j)
        {
            j=s.top();
            s.pop();
            ins[j]=0;
            bd[j]=cnt;
        }
     } 
}

void dagdp()
{
    int i,j;
    queue <int> q;   
    for(i=1;i<=cnt;i++)
    {
        if(!ind[i])  //找到入度为0的点,这个点一定不会被刷新,因此满足dp无后效性 
        {
        q.push(i);
        f[i]=money[i];    
        } 
    }
    while(!q.empty())
    {
        int t=q.front();
        int i,j,k;
        q.pop();
        for(i=head[t];i;i=e[i].next)
        {
        j=e[i].to;
        ind[j]--;  //这个点的入度减一 
        k=money[j];
        f[j]=max(f[t]+k,f[j]);   
        if(!ind[j])  //如果这个点入度为0,那么这个点一定被处理完了 
        q.push(j);  //那么又可以从这个点开始做 
        }
    }
}

int main()
{
    int i,j;
    scanf("%d %d",&n,&m);
    for(i=1;i<=n;i++)
    scanf("%d",&cost[i]);
    for(i=1;i<=m;i++)
    {
        int t1,t2;
        scanf("%d %d",&t1,&t2);
        addedge(t1,t2,0);
        looker[i].next=t1;
        looker[i].to=t2;
    }
    for(i=1;i<=n;i++) if(!dfn[i])tarjan(i);
    memset(head,0,sizeof(head));
    size=0;
    for(i=1;i<=n;i++)
    {
    money[bd[i]]+=cost[i];
    }
    for(i=1;i<=m;i++)
    {
        if(bd[looker[i].next]==bd[looker[i].to]) continue;  //我 到 我自己 ? 
        if(!pd(bd[looker[i].next],bd[looker[i].to])) 
        {
        addedge(bd[looker[i].next],bd[looker[i].to],0);
        ind[bd[looker[i].to]]++; //统计入度与出度 
        chd[bd[looker[i].next]]++;
        }    
    }    
    dagdp();
    for(i=1;i<=cnt;i++) ans=max(ans,f[i]);  //比较每个点与当前最大值
    printf("%d",ans);
    return 0;
}

 

posted @ 2019-06-25 09:05  寒冰大大  阅读(535)  评论(1编辑  收藏  举报