Slow Path Finding Algorithm(多校联考)

题目

小H 今天学习了「缓慢的路径寻找算法」,下课后便准备找一道题练习一下。题目是这样的:给定一张
有向图,每条边上都有一个小写英文字母,小H 需要寻找一条路径使得路径上出现最多的字母的出现次
数最大。然而小H 想了很久也只会jV j = 1 的情形,于是他找到了你,请你帮他解决这个问题。
Input
输入文件包含多组测试数据。
第一行一个整数T (1 T 105),表示测试数据的组数。
每组测试数据的第一行两个整数n, m (1 n 105, 0 m 2 105),分别表示有向图的点数和边数。
接下来m 行,每行两个整数ui, vi (1 ui; vi n) 和一个小写英文字母ci,表示从ui 到vi 有一条有向
边,上面的字母为ci。
保证
Σ
n 106;
Σ
m 2 106。
Output
对于每组测试数据,如果路径上出现最多的字母的出现次数可以是任意大,输出一行-1。
否则,在第一行依次输出一个整数ans,一个字母c 和一个整数k (1 k n),依次表示路径上出现最
多的字母的出现次数,达到最多出现次数的字母以及路径上的点数。
第二行输出k 个整数p1; p2; : : : ; pk (1 pi n),表示这条路径依次经过的点。
如果有多条满足条件的路径,输出任意一条。
Scoring
本题共有5 个测试点,每个测试点20 分。
测试点1:n;m 5。
测试点2:每组测试数据中的ci 均相同。
测试点3:T; n;m 100。
测试点4:保证图中不存在环。
测试点5:无特殊限制。
Page 2 of 5
CSP2019 Training jiangly Contest 1
High School Affiliated to Southwest University, Chongqing, 23 Oct 2019
Example
spfa.in spfa.out
3
1 0
1 1
1 1 a
4 6
1 2 i
1 3 a
1 4 k
2 3 i
2 4 o
3 4 i
0 a 1
1
-1
3 i 4
1 2 3 4
Note
在第一组数据中,只有一个点,没有边,所以唯一的路径就是[1],其中每个字母的出现次数都是0。
在第二组数据中,有一个点和它到自己的一条边,只需要选择路径[1; 1; 1; : : :],就能使得字母a 出现任
意多次。

 

题解

/*
容易想到这是一道dp题,且dp式子也可列出来dp[i][j]表示终点为i的路径其中最多出现字符串是j的次数
那么就可以直接用邻接表转移
那么起点是什么呢?应该是入度为0的点(这道题是Special Judge)
就可以用拓扑排序先把顺序记录下来,在进行dp
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <vector>
#include <stack>
#include <queue>
using namespace std;
#define ll long long
const int MAXN = 1e6 +3;
int n , m;
bool vis[MAXN];
int dp[MAXN][30];
int pre[MAXN][30];
int in[MAXN];
struct node{
    int v;char s;
    node(){}
    node( int V , char W ){
        v= V;s = W;
    }
};
int siz[MAXN] , cnt;
vector<node>G[MAXN];
void read( int &x ){
    x = 0 ;char s = getchar();
    while( s < '0' || s > '9' ){
        s = getchar();
    }
    while( s >='0' && s <= '9' ){
        x = x * 10 + s - '0';
        s = getchar();
    }

}
int tot , ans , last;
char c;
bool flag;
int ne[MAXN];
void TP( ){
    queue<int>q;
    for( int i = 1 ; i <= n ; i ++ ){
        if( !in[i] )
            q.push( i ) , ne[++cnt] = i;
    }
    while( !q.empty() ){
        int x = q.front();q.pop();
        for( int i = 0 ; i < G[x].size() ; i++ ){
            int v = G[x][i].v;
            if( --in[v] == 0 ){
                q.push( v );
                ne[++cnt] = v;
            }
        }
    }
}
int sum[MAXN];
void print( int x , int y ){
    if( x == pre[x][y] ){
        tot ++;
        sum[tot] = x;
        return ;
    }
    print( pre[x][y], y );
    tot ++;
    sum[tot] = x;
}
int main()
{
    //freopen( "spfa.in" , "r" , stdin );
    //freopen( "spfa.out" , "w" , stdout );
    int T;
    scanf( "%d" , &T );
    while( T -- ){
        flag = 0;
        scanf( "%d%d" , &n , &m );
        for( int i = 1 ; i <= n ; i ++ ){
            G[i].clear();
            siz[i] = 0 , vis[i] =0 ;
            sum[i] = 0;in[i] = 0;
            for( int j = 1 ; j <= 26 ; j ++ )
                pre[i][j] = i , dp[i][j] = 0;
        }
        tot = 0;
        for( int i = 1 ; i <= m ; i ++ ){
            int x , y;char p;
            read( x);read( y );scanf( "%c" , &p );
            G[x].push_back( node( y , p ) );
            in[y] +=1;
        }
		tot = 0;
        ans = 0 , last = 1;
        c = 'a';
		cnt = 0;
		TP();
		if( cnt < n ){
            printf( "-1\n" );
            continue;
		}
		for( int i = 1 ; i <= n ; i ++ ){
            int x = ne[i];
            for( int j = 0 ; j < G[x].size() ; j ++ ){
                int v = G[x][j].v , s = G[x][j].s - 96;
                for( int k = 1 ; k <= 26 ; k ++ ){
                    if( dp[v][k] < dp[x][k] + ( s == k  ? 1 :0 ) ){
                        dp[v][k] =  dp[x][k] + ( s == k  ? 1 :0 );
                        pre[v][k] = x;
                        if( ans < dp[v][k] ){
                            ans = dp[v][k];c = k +96;
                            last = v;
                        }
                    }
                }
            }
		}
        printf( "%d %c" , ans , c );
        if( !ans ){
            printf( " 1\n1\n" );
            continue;
        }
        else
            print( last , c - 96 );
        printf( " %d\n" , tot );
        for( int i = 1 ; i <= tot ; i ++ )
            printf( "%d " , sum[i] );
        printf( "\n" );
    }
    return 0;
}

 

总结

这道题最开始想到的是tarjan判环,感觉也可以

但是核心是拓扑排序,这也是关于DAG的题的一种应该想到的思路

拓扑排序还未完全掌握,需要做一些练习

最后一周了,复习一下各种板子

再练一下dp题就可以了...

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页