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題就可以了...

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章