• 洛谷—P3387 【模板】缩点


    题目背景

    缩点+DP

    题目描述

    给定一个n个点m条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大。你只需要求出这个权值和。

    允许多次经过一条边或者一个点,但是,重复经过的点,权值只计算一次。

    输入输出格式

    输入格式:
    第一行,n,m

    第二行,n个整数,依次代表点权

    第三至m+2行,每行两个整数u,v,表示u->v有一条有向边

    输出格式:

    共一行,最大的点权之和。

    输入输出样例

    输入样例#1:
    2 2
    1 1
    1 2
    2 1
    输出样例#1:
    2
    说明 n<=10^4 ,m <=10^5 ,点权 <= 1000
    算法:Tarjan缩点+DAGdp

    思路 : 题目的思路很清晰,都有提示,但是我第一次写的时候 WA了一组 ,T了一组
    在这里插入图片描述
    代码如下 :

    #include <queue>
    #include <stack>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    
    const int maxn = 1e4 + 50;
    const int maxm = 1e5 + 50;
    
    struct edge {
        int nxt ,to ;
    }ss[maxm * 10 ];
    
    stack<int>sta ;
    int head[maxn] ,dfn[maxn] ,low[maxn] ,w[maxn] ;
    int cnt ,Index ,scc ,n ,m ,flag[maxn] ;
    int sum[maxn] ,in[maxn] ,dp[maxn] ;
    bool vis[maxn] ;
    
    void init() {
        memset(head ,-1 ,sizeof(head));
        memset(vis ,false ,sizeof(vis));
    }
    
    inline int Min(int a,int b) { return a > b ? b : a ; }
    inline int Max(int a,int b) { return a > b ? a : b ; }
    
    void add_edge (int u ,int v ){
        ss[++cnt].to = v;
        ss[cnt].nxt = head[u] ;
        head[u] = cnt ;
    }
    
    void tarjan(int x) {
        dfn[x] = low[x] = ++Index ;
        vis[x] = true ; sta.push(x) ;
        for (int i = head[x] ; ~i ; i = ss[i].nxt ) {
            if ( !dfn[ss[i].to] ) {
                tarjan(ss[i].to);
                low[x] = Min(low[x] ,low[ss[i].to]) ;
            } else if ( vis[ss[i].to] ) {
                low[x] = Min(low[x] ,dfn[ss[i].to]) ;
            }
        }
        if ( low[x] == dfn[x] ) {
            scc ++;
            int v;
            do {
                v = sta.top() ; sta.pop();
                flag[v] = scc ;
            } while ( x != v ) ;
        }
    }
    
    void toposort() {
        queue<int >que ;
        for (int i = 1 ; i <= scc ; i ++ ) {
            if( !in[i] ) {
                que.push(i);
                dp[i] = sum[i] ;
            }
        }
    //	for (int i = 1 ; i <= scc ; i ++ ) {
    //		if ( !in[i] ) {
    //			printf("%d %d
    ",i ,sum[i]) ;
    //		}
    //	} puts("") ;
        while ( !que.empty() ) {
            int top = que.front() ;que.pop();
            for (int i = 1 ; i <= n ; i ++ ) {
                if ( flag[i] == top ) {
                    for (int j = head[i] ; ~j ; j = ss[j].nxt ) {
                        if ( flag[i] != flag[ss[j].to] ) {
                            que.push(flag[ss[j].to]) ;
                            dp[flag[ss[j].to]] = Max(dp[flag[ss[j].to]] ,dp[flag[i]] + sum[flag[ss[j].to]]) ;
                        }
                    }
                }
            }
        } 
    }
    
    int main() {
        //FILE *fp;
        //fp = fopen("in.txt" ,"r") ;
        init() ; int u ,v ; 
        //fscanf(fp, "%d %d" ,&n ,&m ) ;
        scanf("%d %d",&n ,&m ) ;
        for (int i = 1 ; i <= n ; i ++ ) {
            //fscanf(fp ,"%d" ,&w[i] );
            scanf("%d",&w[i]);
        }
        for (int i = 1 ; i <= m ; i ++ ) {
            //fscanf(fp ,"%d %d",&u ,&v ) ;
            scanf("%d %d" ,&u ,&v ) ;
            add_edge(u ,v ) ;
        }
        for (int i = 1 ; i <= n ; i ++ ) {
            if ( !vis[i] ) {
                tarjan(i);
            }
        }
        for (int i = 1 ; i <= n ; i ++ ) {
            if ( !flag[i] ) flag[i] = ++ scc ;
        }
    //	for (int i = 1 ; i <= n ; i ++ ) {
    //		printf("%d ",flag[i]) ;
    //	} puts("");
        for (int i = 1 ; i <= n ; i ++ ) {
            for (int j = head[i] ; ~j ; j = ss[j].nxt ) {
                if ( flag[i] != flag[ss[j].to] ) {
                    in[flag[ss[j].to]] ++;
                }
            }
        }
        for (int i = 1 ; i <= n ; i ++ ) {
            sum[flag[i]] += w[i];
        } 
    //	for (int i = 1 ; i <= scc ; i ++ ) {
    //		printf("%d ",sum[i]) ;
    //	} puts("");
        toposort() ;
        int maxx = -0x3f3f3f3f ;
        for (int i = 1 ; i <= scc ; i ++ ) {
            //printf("%d ",dp[i]);
            maxx = Max(maxx ,dp[i] ) ;
        } //puts("");
        printf("%d
    ",maxx) ;
        //fclose(fp) ;
        return 0;
    }
    

    后来想了想,我的那个入度还能够利用一下,因此改了一下
    在这里插入图片描述

    #include <queue>
    #include <stack>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    
    const int maxn = 1e4 + 50;
    const int maxm = 1e5 + 50;
    
    struct edge {
        int nxt ,to ;
    }ss[maxm * 10 ];
    
    stack<int>sta ;
    int head[maxn] ,dfn[maxn] ,low[maxn] ,w[maxn] ;
    int cnt ,Index ,scc ,n ,m ,flag[maxn] ;
    int sum[maxn] ,in[maxn] ,dp[maxn] ;
    bool vis[maxn] ;
    
    void init() {
        memset(head ,-1 ,sizeof(head));
        memset(vis ,false ,sizeof(vis));
    }
    
    inline int Min(int a,int b) { return a > b ? b : a ; }
    inline int Max(int a,int b) { return a > b ? a : b ; }
    
    void add_edge (int u ,int v ){
        ss[++cnt].to = v;
        ss[cnt].nxt = head[u] ;
        head[u] = cnt ;
    }
    
    void tarjan(int x) {
        dfn[x] = low[x] = ++Index ;
        vis[x] = true ; sta.push(x) ;
        for (int i = head[x] ; ~i ; i = ss[i].nxt ) {
            if ( !dfn[ss[i].to] ) {
                tarjan(ss[i].to);
                low[x] = Min(low[x] ,low[ss[i].to]) ;
            } else if ( vis[ss[i].to] ) {
                low[x] = Min(low[x] ,dfn[ss[i].to]) ;
            }
        }
        if ( low[x] == dfn[x] ) {
            scc ++;
            int v;
            do {
                v = sta.top() ; sta.pop();
                flag[v] = scc ;
            } while ( x != v ) ;
        }
    }
    
    void toposort() {
        queue<int >que ;
        for (int i = 1 ; i <= scc ; i ++ ) {
            if( !in[i] ) {
                que.push(i);
                dp[i] = sum[i] ;
            }
        }
        while ( !que.empty() ) {
            int top = que.front() ; que.pop();
            for (int i = 1 ; i <= n ; i ++ ) {
                if ( flag[i] == top ) {
                    for (int j = head[i] ; ~j ; j = ss[j].nxt ) {
                        if ( flag[i] != flag[ss[j].to] ) {
                            in[flag[ss[j].to]] -- ;///这一句
                            if ( !in[flag[ss[j].to]] )///这一句
                                que.push(flag[ss[j].to]) ;///这一句
                            dp[flag[ss[j].to]] = Max(dp[flag[ss[j].to]] ,dp[flag[i]] + sum[flag[ss[j].to]]) ;
                        }
                    }
                }
            }
        } 
    }
    
    int main() {
    //	FILE *fp;
    //	fp = fopen("in.txt" ,"r") ;
        init() ; int u ,v ; 
        //fscanf(fp, "%d %d" ,&n ,&m ) ;
        scanf("%d %d",&n ,&m ) ;
        for (int i = 1 ; i <= n ; i ++ ) {
            //fscanf(fp ,"%d" ,&w[i] );
            scanf("%d",&w[i]);
        }
        for (int i = 1 ; i <= m ; i ++ ) {
            //fscanf(fp ,"%d %d",&u ,&v ) ;
            scanf("%d %d" ,&u ,&v ) ;
            add_edge(u ,v ) ;
        }
        for (int i = 1 ; i <= n ; i ++ ) {
            if ( !vis[i] ) {
                tarjan(i);
            }
        }
        for (int i = 1 ; i <= n ; i ++ ) {
            if ( !flag[i] ) flag[i] = ++ scc ;
        }
    //	for (int i = 1 ; i <= n ; i ++ ) {
    //		printf("%d ",flag[i]) ;
    //	} puts("");
        for (int i = 1 ; i <= n ; i ++ ) {
            for (int j = head[i] ; ~j ; j = ss[j].nxt ) {
                if ( flag[i] != flag[ss[j].to] ) {
                    in[flag[ss[j].to]] ++;
                }
            }
        }
        for (int i = 1 ; i <= n ; i ++ ) {
            sum[flag[i]] += w[i];
        } 
    //	for (int i = 1 ; i <= scc ; i ++ ) {
    //		printf("%d ",sum[i]) ;
    //	} puts("");
        toposort() ;
        int maxx = -0x3f3f3f3f ;
        for (int i = 1 ; i <= scc ; i ++ ) {
            //printf("%d ",dp[i]);
            maxx = Max(maxx ,dp[i] ) ;
        } //puts("");
        printf("%d
    ",maxx) ;
        return 0;
    }
    

    然后就卡在里这里,后面不知道了怎么改,在网上找了一个答案,发现跟我的思路差不多,但是我的就是不对,后来通过这两组数据的缩点对比我发现我的tarjan写错了,最后也没有想到解决方法,就万恶的改了一下自己缩点的模板,最后代码是这样的

    #include <bits/stdc++.h>
    
    using namespace std;
    
    const int maxn = 1e4 + 50 ;
    
    struct edge {
    	int nxt ,u ,v ;
    }ss[maxn * 10] ,s[maxn * 10];
    
    int head[maxn] ,dfn[maxn] ,low[maxn] ,pos ;
    int n ,m ,cnt ,Index ,h[maxn] ,sta[maxn];
    int flag[maxn] ,sum[maxn] ,cnt1 ,in[maxn] ,dp[maxn] ;
    bool vis[maxn] ;
    
    inline int Min(int a,int b) { return a > b ? b : a ; }
    inline int Max(int a,int b) { return a > b ? a : b ; }
    
    void init(){
    	cnt1 = pos = cnt = Index = 0;
    	memset(vis ,false ,sizeof(vis)) ;
    	memset(head ,-1 ,sizeof(head)) ;
    	memset(h ,-1 ,sizeof(h)) ;
    }
    
    void add_edge(int u,int v) {
    	ss[++cnt].u = u ;
    	ss[cnt].v = v;
    	ss[cnt].nxt = head[u] ;
    	head[u] = cnt ;
    }
    
    void tarjan(int x) {
    	dfn[x] = low[x] = ++Index ;
    	vis[x] = true ; sta[++pos] = x;
    	for (int i = head[x] ; ~i ; i = ss[i].nxt ) {
    		if ( !dfn[ss[i].v] ) {
    			tarjan(ss[i].v) ;
    			low[x] = Min(low[x] ,low[ss[i].v]);
    		} else if ( vis[ss[i].v] ) {
    			low[x] = Min(low[x] ,dfn[ss[i].v] ) ;
    		}
    	}
    	if ( low[x] == dfn[x] ) {
    		int y ;
    		while ((y = sta[pos--])) {
    			flag[y] = x;
    			vis[y] = false;
    			if ( x == y ) break;
    			sum[x] += sum[y];
    		} 
    	}
    }
    
    void toposort() {
    	queue<int >que;
    	for (int i = 1 ; i <= n ; i ++ ) {
    		if ( flag[i] == i && !in[i] ) {
    			que.push(i);
    			dp[i] = sum[i] ;
    		}
    	}
    	while( !que.empty() ) {
    		int top = que.front() ; que.pop() ;
    		for (int i = h[top] ; ~i ; i = s[i].nxt ) {
    			dp[s[i].v] = Max(dp[s[i].v] ,dp[top] + sum[s[i].v]) ;
    			in[s[i].v] --;
    			if ( !in[s[i].v] ) que.push(s[i].v) ;
    		}
    	}
    }
    
    int main() {
    	init() ; int u, v ;
    	scanf("%d %d",&n ,&m ) ;
    	for (int i = 1 ; i <= n ; i ++ ) {
    		scanf("%d",&sum[i]);
    	}
    	for ( int i = 1 ; i <= m ; i ++ ) {
    		scanf("%d %d",&u ,&v ) ;
    		add_edge(u ,v ) ;
    	}
    	for (int i = 1 ; i <= n ; i ++ ) {
    		if ( !dfn[i]) tarjan(i);
    	}
    	for (int i = 1 ; i <= m ; i ++ ) {
    		int x = flag[ss[i].u] ,y = flag[ss[i].v] ;
    		if ( x != y ) {
    			s[++cnt1].u = x ;
    			s[cnt1].v = y ;
    			s[cnt1].nxt = h[x] ;
    			h[x] = cnt1;
    			in[y] ++;
    		}
    	}
    	toposort() ;
    	int maxx = -0x3f3f3f3f ;
    	for (int i = 1 ; i <= n ; i ++ ) {
    		if ( dp[i] > maxx ) maxx = dp[i] ;
    	}
    	printf("%d
    ",maxx );
    	return 0;
    }
    
  • 相关阅读:
    bootmgr is conmpressed联想Z485
    数据库中用开窗函数和复制表结构
    左连接新认识
    sql中的日期查询
    一个数据表通过另一个表更新数据(在UPDAT语句中使用FROM子句)
    StarUML安装与Win7不兼容解决
    SQL中VARCHAR与NVARCHAR存储区别
    Cannot start service SPUserCodeV4 on computer
    SQL中空值与NULL区别
    验证码破解分析
  • 原文地址:https://www.cnblogs.com/Nlifea/p/11745922.html
Copyright © 2020-2023  润新知