我们以洛谷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; }