首先,从模板题开始学起——
思路:
1. 这道题为什么要缩点?(什么时候需要缩点)
根据题目意思,我们只需要找出一条点权最大的路径就行了,不限制点的个数。那么考虑对于一个环上的点被选择了,一整条环是不是应该都被选择,这一定很优,能选干嘛不选。很关键的是题目还允许我们重复经过某条边或者某个点,我们就不需要考虑其他了。因此整个环实际上可以看成一个点(选了其中一个点就应该选其他的点)
——摘录自洛谷第一篇题解
2. 将强连通分量缩点 -> 建边成为DAG -> DPmax

1 #include<cstdio> 2 #include<queue> 3 #include<vector> 4 #include<cstring> 5 #include<algorithm> 6 #define N 100010 7 #define M 500010 8 using namespace std; 9 10 struct node{ 11 int from,to,next; 12 }edge[M]; 13 queue < int > q; 14 vector < int > cd[N]; //出度 15 vector < int > rd[N]; //入度 16 int ans[M],t,x,y,v,rds[N],u,n,m,sum,vis[N],d[N],dis[N]; 17 int dfn[N],low[N],f[N],time,cnt,k; 18 int stack[N],head[M],visit[N],tot,id; 19 20 void add(int x,int y){ //邻接表加边 21 edge[++cnt].next=head[x]; 22 edge[cnt].from=x; 23 edge[cnt].to=y; 24 head[x]=cnt; 25 } 26 27 void tarjan(int x){ 28 dfn[x]=low[x]=++time; //更新时间 29 stack[++id]=x; //手写栈 30 visit[x]=1; //入栈 31 for(int i=head[x];i;i=edge[i].next){ 32 if(!dfn[edge[i].to]){ //没有更新到的点 33 tarjan(edge[i].to); 34 low[x]=min(low[x],low[edge[i].to]); 35 } 36 else{ 37 if(visit[edge[i].to]) //更新过的点 38 low[x]=min(low[x],dfn[edge[i].to]); 39 } 40 } 41 if(low[x]==dfn[x]){ //注意不在 for 循环内 42 tot++; //强连通分量编号 (缩点的编号) 43 while(1){ 44 vis[stack[id]]=tot; //vis记录缩点的编号 45 dis[tot]+=d[stack[id]]; //缩点的权值=强连通分量权值累加 46 visit[stack[id]]=0,id--; //出栈 47 if(x==stack[id+1]) break; //这个连通块已弹完 48 } 49 } 50 } 51 void topo(){ //拓扑排序 52 for(int i=1;i<=tot;i++) if(rds[i]==0) q.push(i); //入度数(rds)为 0 ,进队 53 while(!q.empty()){ 54 int u=q.front(); 55 q.pop(); //队头出队 56 ans[++k]=u; //ans记录按拓扑序排列的点 57 for(int i=1;i<=cd[u].size();i++){ //cd[u].size(): cd[u][]的长度 58 v=cd[u][i-1]; //因为 vector是从 0开始的,所以减 1 59 rds[v]--; //入度数-- 60 if(rds[v]==0) q.push(v); //入度数(rds)为 0 ,进队 61 } 62 } 63 } 64 65 int main() 66 { 67 scanf("%d%d",&n,&m); 68 for(int i=1;i<=n;i++) scanf("%d",&d[i]); 69 for(int i=1;i<=m;i++) scanf("%d%d",&x,&y),add(x,y); 70 71 for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i); // tarjan缩点 72 73 for(int i=1;i<=cnt;i++){ //把缩好的点 建边 ->DAG 74 if(vis[edge[i].from]!=vis[edge[i].to]){ 75 x=vis[edge[i].from],y=vis[edge[i].to]; // 建 x->y 的边 76 rds[y]++; // y 的入度数++ 77 rd[y].push_back(x); // 把 x 放入 y 的入度 78 cd[x].push_back(y); // 把 y 放入 x 的出度 79 } 80 } 81 topo(); // DAG上跑拓扑 82 for(int i=1;i<=tot;i++){ //按照拓扑序(无后效性) 跑 DP 83 int w=ans[i]; 84 f[w]=dis[w]; 85 for(int j=1;j<=rd[w].size();j++) 86 f[w]=max(f[w],f[rd[w][j-1]]+dis[w]); //因为 vector是从 0开始的,所以减 1 87 } 88 89 for(int i=1;i<=tot;i++) sum=max(f[i],sum); //最后统计答案 90 printf("%d",sum); 91 return 0; 92 }

1 #include<cstdio> 2 #include<algorithm> 3 #define N 100010 4 using namespace std; 5 struct node{ 6 int next,to; 7 }edge[N*2]; 8 int n,m,dfn[N],cut[N],head[N],low[N],tot,cnt,id; 9 10 void add(int x,int y){ 11 edge[++cnt].next=head[x]; 12 edge[cnt].to=y; 13 head[x]=cnt; 14 } 15 16 void tarjan(int x,int root){ //割点 17 int child=0; 18 dfn[x]=low[x]=++id; 19 for(int v,i=head[x];i;i=edge[i].next){ 20 v=edge[i].to; 21 if(!dfn[v]){ //没有更新过的 22 tarjan(v,root); 23 low[x]=min(low[x],low[v]); 24 if(low[v]>=dfn[x]&&x!=root) cut[x]=1; //x不为根的割点判断条件: low[v]>=dfn[x] 25 if(x==root) child++; //若 x为根 26 } 27 else if(x!=root) low[x]=min(low[x],dfn[v]); 28 } 29 if(child>=2&&x==root) cut[x]=1; //x为根的割点判断条件:有2棵及以上的子树 30 } 31 int main() 32 { 33 scanf("%d%d",&n,&m); 34 for(int u,v,i=1;i<=m;i++){ 35 scanf("%d%d",&u,&v); 36 add(u,v),add(v,u); 37 } 38 for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i,i); 39 for(int i=1;i<=n;i++) if(cut[i]) tot++; 40 printf("%d ",tot); 41 for(int i=1;i<=n;i++) if(cut[i]) printf("%d ",i); 42 return 0; 43 }
Then ,我就找了一道题练手 —— 矿场搭建
(先把这题的思路和注释咕咕咕一下趴)

1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #define N 510 5 using namespace std; 6 int head[N],dfn[N],low[N],visit[N]; 7 bool cut[N]; 8 long long group,cuts,num,t,m,root,cnt,n,time,ans1,ans2,child; 9 struct node{ 10 int to,next; 11 }edge[N*2]; //无向图注意*2 12 void add(int x,int y){ 13 edge[++cnt].next=head[x]; 14 edge[cnt].to=y; 15 head[x]=cnt; 16 } 17 void tarjan(int x,int root){ 18 dfn[x]=low[x]=++time; 19 for(int v,i=head[x];i;i=edge[i].next){ 20 v=edge[i].to; 21 if(!dfn[v]){ 22 tarjan(v,root); 23 low[x]=min(low[x],low[v]); 24 if(low[v]>=dfn[x]&&x!=root) cut[x]=1; 25 if(x==root) child++; 26 } 27 else if(root!=x) low[x]=min(low[x],dfn[v]); 28 } 29 if(x==root&&child>=2) cut[x]=1; 30 } 31 void dfs(int x){ 32 visit[x]=group,++num; 33 for(int v,i=head[x];i;i=edge[i].next){ 34 v=edge[i].to; 35 if(cut[v]&&visit[v]!=group){ 36 cuts++; 37 visit[v]=group; 38 } 39 else if(!visit[v]) dfs(v); 40 } 41 } 42 int main() 43 { 44 while(scanf("%d",&m)&&m){ 45 t++; 46 ans1=0,ans2=1,num=0,time=0,cnt=0,n=0; 47 memset(head,0,sizeof(head)); 48 memset(dfn,0,sizeof(dfn)); 49 memset(low,0,sizeof(low)); 50 memset(cut,0,sizeof(cut)); 51 memset(visit,0,sizeof(visit)); //初始化 52 long long u,v; 53 54 for(long long i=1;i<=m;i++){ 55 scanf("%lld%lld",&u,&v); 56 add(u,v),add(v,u); //无向图 57 n=max(max(v,u),n); //求点数 58 } 59 60 for(long long i=1;i<=n;i++){ 61 if(!dfn[i]) child=0,tarjan(i,i); 62 for(int i=1;i<=n;i++){ 63 if(!visit[i]&&!cut[i]){ 64 ++group; 65 num=cuts=0; 66 dfs(i); 67 if(cuts==0) ans1+=2,ans2*=(num-1)*num/2; 68 else if(cuts==1) ans1++,ans2*=num;//printf(" 111 "); 69 } 70 } 71 } 72 printf("Case %lld: %lld %lld ",t,ans1,ans2); 73 } 74 return 0; 75 }
通过这道题我发现我对tarjan的理解还不够透彻(不知如何变换),代码也没记牢,还需多加练习!
注意 : 字典序的处理

1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #define N 5010 5 #define M 50010 6 using namespace std; 7 int read(){ 8 int x=0,f=1; char c=getchar(); 9 while(c<'0'||c>'9') {if(c=='-')f=-1; c=getchar();} 10 while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar(); 11 return x*f; 12 } 13 struct node{ 14 int next,to; 15 }edge[M*2]; 16 int n,m,cnt,tot,time,id,maxn; 17 int dfn[N],low[N],head[M*2],vis[N],visit[N],stack[N],size[N]; 18 void add(int x,int y){ 19 edge[++cnt].next=head[x]; 20 edge[cnt].to=y; 21 head[x]=cnt; 22 } 23 void tarjan(int x){ 24 dfn[x]=low[x]=++time; 25 stack[++id]=x; 26 visit[x]=1; 27 for(int i=head[x];i;i=edge[i].next){ 28 if(!dfn[edge[i].to]){ 29 tarjan(edge[i].to); 30 low[x]=min(low[x],low[edge[i].to]); 31 } 32 else{ 33 if(visit[edge[i].to]) 34 low[x]=min(low[x],dfn[edge[i].to]); 35 } 36 } 37 if(low[x]==dfn[x]){ 38 tot++; 39 while(1){ 40 vis[stack[id]]=tot; 41 size[tot]++; //printf("%d ",size[tot]); 42 visit[stack[id]]=0,id--; 43 if(x==stack[id+1]) break; 44 } 45 } 46 } 47 int main() 48 { 49 n=read(),m=read(); 50 for(int i=1;i<=m;i++){ 51 int u=read(),v=read(),t=read(); 52 if(t==1) add(u,v); 53 else add(u,v),add(v,u); 54 } 55 56 for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i); 57 58 for(int i=1;i<=tot;i++) maxn=max(maxn,size[i]);//printf("* %d ",size[i]); 59 printf("%d ",maxn); 60 for(int i=1;i<=n;i++){ 61 if(size[vis[i]]==maxn){ 62 int now=vis[i]; 63 for(int j=i;j<=n;j++) 64 if(vis[j]==now) printf("%d ",j); 65 return 0; 66 } 67 } 68 return 0; 69 }