複試機試常用算法複習

kmp

子串
kmp模板,偷懶直接find()了 0.0

#include <bits/stdc++.h>
using namespace std;
int main() {
	int n ;
	string s , t ;
	cin >> n >> t ; 
	vector<int>v ;
	for( int k = 2 ; k <= 16 ; k++ ) {
		s = "" ;
		for( int i = 1 ; i <= n ; i++ ) {
			int tmp = i ;
			while( tmp ) {
				v.push_back( tmp % k ) ;
				tmp /= k ;
			}
			for( int i = v.size() - 1 ; i >= 0 ; i-- ) {
				if( v[i] >= 10 ) {
					s += (char)( v[i] - 10 + 'A' ) ;
				} else {
					s += (char)( v[i] + '0' ) ;
				}
			}
			v.clear() ;
		}
		int pos = s.find(t) ;
		if( pos >= 0 && pos < s.size() ) {
			cout << "yes" << endl ;
			return 0 ;
		}
	}
	cout << "no" << endl;
	return 0 ;
}

慄醬的數列
思路:kmp+差分
滿足( a1 + b1 ) % k == ( a2 + b2 ) % k , 與滿足 ( a2 - a1 == b1 - b2 ) 一樣
轉換之後套kmp,找總共幾個串滿足

#include <bits/stdc++.h>
using namespace std;
#define LL long long 
const int AX = 2e5 + 666 ;
LL a[AX] ;
LL b[AX] ;
int nxt[AX] ;
int res , n , m ; 
void get_next(){
	nxt[0] = -1 ;
	int j = 0 , k = -1 ; 
	while( j < m ){
		if( k == -1 || b[k] == b[j] ){
			nxt[++j] = ++k ;
		}else{
			k = nxt[k] ; 
		}
	}
}

void kmp(){
	int i = 0 , j = 0 ;
	while( i < n && j < m ){
		if( j == -1 || a[i] == b[j] ){
			i ++ ;
			j ++ ;
		}else j = nxt[j] ; 
		if( j == m ){   // 
			res ++ ;
			j = nxt[j] ; 
		}
	}
}

int main(){
	int T ;
	LL	k ;
	cin >> T ;
	while( T-- ){
		res = 0 ;
		memset( nxt , 0 ,sizeof(nxt) ) ;
		cin >> n >> m >> k ;
		for( int i = 0 ; i < n ; i++ ){
			cin >> a[i] ;
		}
		for( int i = 0 ; i < m ; i++ ){
			cin >> b[i] ;
		}
		for( int i = 1 ; i < n ; i++ ){
			a[i-1] = ( ( a[i-1] - a[i] ) % k + k ) % k ;
		}
		for( int i = 1 ; i < m ; i++ ){
			b[i-1] = ( ( b[i] - b[i-1] ) % k + k ) % k ;
		}
		m --;
		n --;
		get_next();
		kmp();
		cout << res << endl ;
	}
	return 0 ;
}

manacher

迴文串
Manacher 模板

#include <bits/stdc++.h>
using namespace std;
const int AX = 3e3 + 666 ;
char s[AX] ;
int p[AX] ;
int main(){
	while( ~scanf("%s",s) ){
		int len = strlen(s) ;
		for( int i = len ; i >= 0 ; i-- ){
			s[2*i+1] = '#' ;
			s[2*i+2] = s[i] ;
		}
		s[0] = '$' ;
		int id = 0 , mx = 0 ; 
		len *= 2 ; 
		memset( p , 0 , sizeof(p) );
		int res = 0 ;
		for( int i = 2 ; i <= len ; i++ ){
			if( p[id] + id > i ) p[i] = min( p[id] + id - i , p[2*id-i] );
			else p[i] = 1 ;
			while( s[i-p[i]] == s[i+p[i]] ) p[i]++ ;
			if( p[i] + i > mx ){
				mx = p[i] + i ; 
				id = i ;
			}
			res = max( res , p[i] );
		}
		printf("%d\n",res-1);
	}
	return 0 ;
}

最長迴文
思路:要求兩個串的子串拼起來爲迴文 的最大長度,首先分別對兩個串求最長迴文,枚舉迴文中心,然後向兩邊擴展,A的左邊 == B的右邊,就擴展。

#include <bits/stdc++.h>
using namespace std;
const int AX = 2e5 + 666 ;
char a[AX] , b[AX] ;
int pa[AX] , pb[AX] ;
int n ;
void manacher( char *s , int *p ){
	for( int i = n ; i >= 0 ; i-- ){
		s[2*i+2] = s[i] ;
		s[2*i+1] = '#' ;
	}
	s[0] = '$';
	int id = 0 , mx = 0 ; 
	for( int i = 2 ; i <= 2 * n ; i++ ){
		if( p[id] + id > i ) p[i] = min( p[id] + id - i , p[2*id-i] ) ;
		else p[i] = 1 ;
		while( s[i+p[i]] == s[i-p[i]] ) p[i] ++ ;
		if( i + p[i] > mx ){
			mx = p[i] + i ; 
			id = i ;
		}
	}
}
int main() {
	cin >> n ;
	cin >> a >> b ;
	manacher( a , pa ); 
	manacher( b , pb );
	int res = 0 ; 
	for( int i = 2 ; i <= 2 * n ; i++ ){
		int tmp = max( pa[i] , pb[i-2] );
		while( a[i-tmp] == b[i+tmp-2] ) tmp ++ ;
		res = max( res , tmp );
	}
	cout << res - 1 << endl;
	return 0 ;
}

Tarjan

可達性

/*
求出若干個強連通分量後,進行縮點:
    將每個強連通分量看做DAG圖中的一個點,建立各點之間的邊(本題直接統計各點(連通分量)入度)
    入度爲0的強連通分量點集中取最小的點 構成所求集合
*/
#include <bits/stdc++.h>
using namespace std ;
const int AX = 1e5 + 666 ;
struct Node {
	int u , v , nxt ;
	Node() {}
	Node( int u , int v , int nxt ):u(u),v(v),nxt(nxt) {}
} G[AX];
vector<int>res ;
int head[AX] ;
int dfn[AX] ;
int low[AX] ;
int vis[AX] ;
int scc_id[AX] ;
int tot , cnt ;
int scc ; 
int in[AX] ;
vector<int>id[AX] ;  // 強連通分量集合
void add( int u , int v ) {
	G[tot] = Node( u , v , head[u] ) ;
	head[u] = tot ++ ;
}
stack<int>s;
void Tarjan( int u ) {
	dfn[u] = low[u] = ++cnt ;
	s.push(u);
	vis[u] = 1 ;
	for( int i = head[u] ; ~i ; i = G[i].nxt ) {
		int v = G[i].v ;
		if( !dfn[v] ) {
			Tarjan( v ) ;
			low[u] = min( low[u] , low[v] ) ;
		}else if( vis[v] ){
			low[u] = min( low[u] , low[v] ) ;
		}
	}
	if( dfn[u] == low[u] ) {
		int v = 0 ;
		while( u != v ){
			vis[s.top()] = 0 ;
			v = s.top() ; 
			scc_id[v] = scc ; 
			id[scc].push_back(v);
			s.pop() ; 
		}
		scc ++ ; 
	}
}
int main() {
	int n , m , x , y ;
	scanf("%d%d",&n,&m);
	cnt = tot = 0 ;
	scc = 0 ;
	memset( head , -1 , sizeof(head) ) ;
	memset( dfn , 0 , sizeof(dfn) ) ;
	memset( vis , 0 , sizeof(vis) ) ;
	memset( in , 0 , sizeof(in) ) ;
	for( int i = 0 ; i < m ; i++ ) {
		scanf("%d%d",&x,&y);
		add( x , y );
	}
	for( int i = 1 ; i <= n ; i++ ) {
		if( !dfn[i] ) {
			Tarjan( i ) ;
		}
	}
	
	for( int i = 1 ; i <= n ; i++ ){
		for( int j = head[i] ; ~j ; j = G[j].nxt ){
			if( scc_id[G[j].v] != scc_id[i] ){
				in[scc_id[G[j].v]]++ ; 		// 縮點
			}
		}
	}
	for( int i = 0 ; i < scc ; i++ ){
		sort( id[i].begin() , id[i].end() );
	}
	for( int i = 0 ; i < scc ; i++ ){
		if( !in[i] ) res.push_back(id[i][0]); // 入度0的最小點
	}
	sort( res.begin() , res.end() );
	int len = res.size() ;
	printf("%d\n",len);
	for( int i = 0 ; i < len ; i++ ) {
		printf("%d ",res[i]);
	}
	printf("\n");
	return 0 ;
}

Blockade
思路:去掉割點會造成若干個集合之間的點不能連接,去掉普通的點只會造成1個點與其餘所有點不能連接。
故本題關鍵求割點對答案的貢獻,有三部分:
一是和普通點一樣,去掉的點與其餘各點( n-1 ) * 2
二是去掉割點後,割點的 每個 子連通塊與其餘節點的數對
三是去掉割點後,割點所有上部的節點其餘節點的數對

#include <bits/stdc++.h>
#define LL long long 
using namespace std;
const int AX = 1e5 + 66 ;
int head[AX] ;
int dfn[AX] ;
int low[AX] ; 
LL res[AX] ; 
int num[AX] ;
int tot , cnt ;
int n , m ;
struct Node{
	int v , nxt ; 
	Node(){}
	Node( int v , int nxt ):v(v),nxt(nxt){}
}G[1000010] ;
void add( int u , int v ){
	G[tot] = Node( v , head[u] ) ;
	head[u] = tot ++ ;
	G[tot] = Node( u , head[v] ) ;
	head[v] = tot ++ ;
}

void Tarjan( int u , int pre ){
	dfn[u] = low[u] = ++cnt ;
	res[u] += 2LL * ( n - 1 ) ; // 1. 被去掉的點到其他各點
	num[u] = 1 ; 
	LL sum = 0 ;
	for( int i = head[u] ; ~i ; i = G[i].nxt ){
		int v = G[i].v ; 
		if( !dfn[v] ){
			Tarjan( v , u ) ;
			
			num[u] += num[v] ;	// u節點及子樹節點數目
			
			low[u] = min( low[u] , low[v] ) ;
			
			if( low[v] >= dfn[u] ){ // u爲割點
				res[u] += 1LL * num[v] * ( n - num[v] - 1 ) ;  // 2. 割點去除後的每個子連通塊同其他連通塊的數對
				
				sum += num[v] ; // 割點子樹節點數目
			}
			
		}else if( v != pre ){
			low[u] = min( low[u] , dfn[v] );
		}
	}
	res[u] += 1LL * sum * ( n - sum - 1 ) ; // 3.sum爲割點子樹的所有點構成的連通塊節點數目(tarjan在求割點的過程中是一棵搜索樹往下遍歷)
}

int main() {
	int u , v ;
	memset( head , -1 , sizeof(head) ) ;
	memset( res , 0 , sizeof(res) );
	memset( low , 0 , sizeof(low) );
	memset( dfn , 0 , sizeof(dfn) );
	memset( num , 0 , sizeof(num) );
	tot = cnt = 0 ; 
	scanf("%d%d",&n,&m);	
	for( int i = 0 ; i < m ; i++ ) {
		scanf("%d%d",&u,&v);
		add( u , v ) ;
	}
	Tarjan( 1 , 0 ) ;
	for( int i = 1 ; i <= n ; i++ ){
		printf("%lld\n",res[i]);
	}
	return 0 ;
}

最小生成樹

1.道路建設
思路:模板

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
const int AX = 2e2 + 66 ;
int a[AX][AX];
int dis[AX] ;
int vis[AX] ;
int c , n , m , x , y , w ;
int prim() {
	int res = 0 ;
	memset( dis , 0x3f , sizeof(dis) ) ;
	memset( vis , 0 , sizeof(vis) ) ;
	dis[1] = 0 ;
	for( int i = 1 ; i <= n ; i++ ) {
		int minu = INF , id = 1 ;
		for( int j = 1 ; j <= n ; j++ ) {
			if( !vis[j] && minu > dis[j] ) {
				id = j ;
				minu = dis[j] ;
			}
		}
		if( minu == INF ) return -1 ;
		res += minu ;
		vis[id] = 1 ;
		for( int j = 1 ; j <= n ; j++ ) {
			if( !vis[j] && dis[j] > a[id][j] ) {
				dis[j] = a[id][j] ;
			}
		}
	}
	return res ;
}
int main() {
	while( ~scanf("%d%d%d",&c,&m,&n) ) {
		memset( a , 0x3f , sizeof(a) ) ;
		for( int i = 0 ; i < m ; i++ ) {
			scanf("%d%d%d",&x,&y,&w);
			a[x][y] = min( a[x][y] , w ) ;
			a[y][x] = a[x][y] ;
		}
		int res = prim();
		//cout << res << endl;
		if( res != -1 && res <= c ) printf("Yes\n");
		else printf("No\n");
	}
	return 0 ;
}

2.[HAOI2006]聰明的猴子
思路:找最小生成樹的最大邊。

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define eps 1e-10
using namespace std ;
const int AX = 1e3 + 66 ;
struct Node {
	double x , y ;
} G[AX] ;
int n , m ;
double get_dis( Node a , Node b ) {
	return sqrt( ( a.x - b.x ) * ( a.x - b.x ) + ( a.y - b.y ) * ( a.y - b.y ) ) ;
}
double a[AX] ;
double dis[AX] ;
int vis[AX] ;
double ans ;
void prim() {
	for( int i = 0 ; i < n ; i++ ){
		dis[i] = INF ;
	}
	memset( vis , 0 , sizeof(vis) ) ;
	dis[0] = 0 ;
	for( int i = 1 ; i <= n ; i++ ) {
		double minu = INF ;
		int id = 0 ;
		for( int j = 0 ; j < n ; j++ ) {
			if( !vis[j] && minu > dis[j] ) {
				minu = dis[j] ;
				id = j ;
			}
		}
		if( minu == INF ) return ;
		
		if( minu > ans ) {
			ans = minu ; 
		}
		
		vis[id] = 1 ;
		for( int j = 0 ; j < n ; j++ ) {
			double tmp = get_dis( G[id] , G[j] ) ;
			if( !vis[j] && dis[j] > tmp ) {
				dis[j] = tmp ;
			}
		}
	}
}
int main() {
	scanf("%d",&m);
	for( int i = 0 ; i < m ; i++ ) {
		scanf("%lf",&a[i]) ;
	}
	scanf("%d",&n);
	for( int i = 0 ; i < n ; i++ ) {
		scanf("%lf%lf",&G[i].x,&G[i].y);
	}
	ans = 0 ;
	prim();
	int res = 0 ; 

	for( int i = 0 ; i < m ; i++ ){
		if( a[i] >= ans ) res ++ ; 
	}
	printf("%d\n",res);
	return 0 ;
}

貨車運輸
思路:最大生成樹+LCA
先求出最大生成樹,再通過Tarjan算法,利用minu[x]記錄點x到其 目前集合根節點 路徑中 邊的最小值
每次訪問完以u爲根的樹後,處理查詢(x,y),且處理的查詢滿足 x , y 的LCA都爲u
其路徑中最短邊長度是 合併集合後 的 min(minu[x],minu[y])

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
typedef pair<int,int>P;
const int AX = 5e4 + 66 ;
int n , m ;
int tot ;
struct Edge {
	int u , v , w ;
	Edge( int u , int v , int w ):u(u),v(v),w(w) {}
	bool operator < ( const Edge &a )const {
		return w > a.w ;
	}
};
struct Node {
	int u , v , w , nxt ;
	Node() {}
	Node( int u , int v , int w , int nxt ):u(u),v(v),w(w),nxt(nxt) {}
} G[AX] ;

struct RES{
	int x , y , id ;
	RES( int x , int y , int id ):x(x),y(y),id(id){}
};
vector<RES>t_u[AX] ;
vector<Edge>e;
vector<P>que[AX] ;
int res[AX] ; 
int head[AX] ; 
int vis[AX] ;
int pre[AX] ;
int minu[AX] ;
void add( int u , int v , int w ){
	G[tot] = Node( u , v , w , head[u] ) ; head[u] = tot ++ ;
	G[tot] = Node( v , u , w , head[v] ) ; head[v] = tot ++ ;
}

int find( int x ){
	//return x == pre[x] ? x : pre[x] = find(pre[x]) ; 
	int tmp = pre[x] ; 
	if( x != tmp ){
		pre[x] = find(pre[x]);
	} 
	minu[x] = min( minu[x] , minu[tmp] );
	return pre[x] ; 
}
void mix( int x , int y ){
	x = find(x) ;
	y = find(y) ;
	if( x != y ){
		pre[x] = y ;
	}
}
void Tarjan( int u ){
	vis[u] = 1 ; 
	for( int i = head[u] ; ~i ; i = G[i].nxt ){
		int v = G[i].v ;
		if( vis[v] ) continue ; 
		Tarjan(v) ;
		minu[v] = G[i].w ; 
		pre[v] = u ;
	}
	for( int i = 0 ; i < (int)que[u].size() ; i++ ){ // 訪問完以u爲根的所有子樹後,處理與u有關的詢問 
		int v = que[u][i].first ; 
		int id = que[u][i].second ; 
		if( !vis[v] ) continue ;
		t_u[find(v)].push_back(RES(u,v,id));		// 與u相關的查詢(u,v),加入到find(v)集合去處理
 	}
	for( int i = 0 ; i < t_u[u].size() ; i++ ){ // 處理所有LCA爲u的查詢(x,y),其路徑中最短邊長度是合併集合後的min(minu[x],minu[y])
		int id = t_u[u][i].id ; 
		int x = t_u[u][i].x ;
		int y = t_u[u][i].y ;
		find(x) ; find(y) ;
		res[id] = min( minu[x] , minu[y] );
	}
}
int main() {
	int x , y , w , q ;
	tot = 0 ;
	scanf("%d%d",&n,&m);
	for( int i = 1 ; i <= n ; i++ ){
		pre[i] = i ;
	}
	memset( head , -1 , sizeof(head) );
	memset( res , -1 , sizeof(res) ) ;
	memset( vis , 0 , sizeof(vis) ) ;
	while( m-- ) {
		scanf("%d%d%d",&x,&y,&w);
		e.push_back(Edge(x,y,w));
		e.push_back(Edge(y,x,w));
	}
	scanf("%d",&q);
	for( int i = 0 ; i < q ; i++ ) {
		scanf("%d%d",&x,&y);
		que[x].push_back(P(y,i));
		que[y].push_back(P(x,i));
	}
	sort( e.begin() , e.end() ) ;
	for( int i = 0 ; i < (int)e.size() ; i++ ){
		int u = e[i].u ;
		int v = e[i].v ; 
		if( find(u) != find(v) ){
			mix( u ,v ) ; 
			add( u , v , e[i].w );
		}
	}
	for( int i = 1 ; i <= n ; i++ ){
		pre[i] = i ;
	}
	memset( minu , 0x3f , sizeof(minu) );
	Tarjan(1);
	for( int i = 0 ; i < q ; i++ ){
		printf("%d\n",res[i]);
	}
	return 0 ;
}

【注】:這個題目求任意兩點路徑中的最小值,最大值同理。
另外,這個思路可以作爲求次小生成樹的更優解法。常規做法見次小生成樹 , 利用這題的思路時間上可以更優: 枚舉不在最小生成樹中的邊<x,y>,查詢生成樹中x到y路徑中的最大邊長度,去除此最大長度,加上邊<x,y>,取最小值

最短路

Travel
思路:點u到點v有兩種方式:
一是不經過傳送,只在環上逆時針或者順時針走
二是會用到傳送門(經過傳送點x最短路徑就是u到x最短路+x到v最短路
兩種情況取最小值就是答案

#include <bits/stdc++.h>
#define LL long long
#define INF 0x3f3f3f3f3f3f3f3f
using namespace std;
typedef pair<LL,int>P;
const int AX = 6e4 + 666 ;
int tot ;
int head[AX] ;
LL dis[50][AX] ;
LL pre[AX];
struct Node {
	int u , v , nxt ;
	LL w ;
	Node() {}
	Node( int u , int v , LL w , int nxt ):u(u),v(v),w(w),nxt(nxt) {}
} G[AX<<1];
int vis[AX] ;

void add( int u , int v , LL w ) {
	G[tot] = Node( u , v , w , head[u] ) ;
	head[u] = tot++ ;
	G[tot] = Node( v , u , w , head[v] ) ;
	head[v] = tot++ ;
}
void dijstra( int id ) {
	memset( vis , 0 ,sizeof(vis) ) ;
	int s = t[id] ;
	dis[id][s] = 0 ;
	priority_queue<P,vector<P>, greater<P> >q ;
	q.push(P(0,s)) ;
	while( !q.empty() ) {
		P tmp = q.top() ;
		q.pop() ;
		int u = tmp.second ;
		if( vis[u] ) continue ;
		vis[u] = 1 ;
		for( int i = head[u] ; ~i ; i = G[i].nxt ) {
			int v = G[i].v ;
			if( !vis[v] && dis[id][v] > dis[id][u] + G[i].w ) {
				dis[id][v] = dis[id][u] + G[i].w ;
				q.push(P(dis[id][v],v));
			}
		}
	}
}
int main() {
	int n , m , x , y ,q ;
	LL w ;
	scanf("%d%d",&n,&m);
	tot = 0 ;
	memset( head , -1 , sizeof(head) ) ;
	memset( dis , 0x3f , sizeof(dis) ) ;
	pre[0] = 0 ;
	for( int i = 1 ; i <= n ; i++ ) {
		scanf("%d",&x) ;
		add( i , ( i % n ) + 1 , x ) ;
		pre[i] = pre[i-1] + x ;
	}
	map<int,int>mp;
	while( m-- ) {
		scanf("%d%d%lld",&x,&y,&w) ;
		if( x == y ) continue ;
		add( x , y , w ) ;
		if( !mp[x] ) t.push_back(x);
		if( !mp[y] ) t.push_back(y);
		mp[x] = 1 ;
		mp[y] = 1 ;
	}
	int len = t.size() ;
	for( int i = 0 ; i < len ; i++ ) {  // 求出帶傳送的點到其餘各點距離
		dijstra( i ) ;
	}
	scanf("%d",&q);
	while( q-- ) {
		scanf("%d%d",&x,&y);
		LL res = min( abs(pre[y-1] - pre[x-1]), pre[n] - abs(pre[y-1] - pre[x-1]) ) ; // 不經過傳送,順逆時針
		for( int i = 0 ; i < len ; i++ ) {
			res = min( res , dis[i][x] + dis[i][y] ) ;  // 經過傳送
		}
		printf("%lld\n",res);
	}
	return 0 ;
}

最短路
思路:有負權無負環,用SPFA

#include <bits/stdc++.h>
using namespace std;
const int AX = 2e4 + 666 ;
int n , m ;
struct Node{
	int to , w ; 
	Node( int to , int w ):to(to),w(w){}
};
vector<Node>G[AX] ;
int vis[AX] ; 
int dis[AX] ; 
void spfa(){
	queue<int>q;
	q.push(1);
	vis[1] = 1 ;
	memset( dis , 0x3f , sizeof(dis) ) ;
	dis[1] = 0 ; 
	while( !q.empty() ){
		int u = q.front() ;
		q.pop() ; 
		vis[u] = 0 ; 
		for( int i = 0 ; i < G[u].size() ; i++ ){
			int v = G[u][i].to ; 
			if( dis[v] > dis[u] + G[u][i].w ){
				dis[v] = dis[u] + G[u][i].w ;
				if( !vis[v] ){
					vis[v] = 1 ;
					q.push(v);
				}
			}
		}
	}
}
int main(){
	scanf("%d%d",&n,&m);
	int x , y , w ; 
	while( m-- ){
		scanf("%d%d%d",&x,&y,&w);
		G[x].push_back(Node(y,w));
	}
	memset( vis , 0 , sizeof(vis) ) ;
	spfa();
	for( int i = 2 ; i <= n ; i++ ){
		printf("%d\n",dis[i]);
	}
	return 0 ; 
}

旅行
思路:枚舉中間點,求到各點距離,取兩段長度和的最大值。

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
typedef pair<int,int>P;
const int AX = 1e3 + 66;
struct Node {
	int v , w , nxt ;
	Node() {}
	Node( int v , int w , int nxt ):v(v),w(w),nxt(nxt) {}
} G[AX<<1] ;
int head[AX] ;
int vis[AX] ;
int tot ;
int dis[AX] ;
vector<int>ans ;
void add( int u , int v , int w ) {
	G[tot] = Node( v , w , head[u] ) ;
	head[u] = tot ++ ;
	G[tot] = Node( u , w , head[v] ) ;
	head[v] = tot ++ ;
}
bool cmp( int a , int b ) {
	return a > b ;
}
int n , m , x , y , w ;
int dijstra( int s ) {
	priority_queue< P , vector<P> , greater<P> >q ;
	q.push(P(0,s)) ;
	memset( vis , 0 , sizeof(vis) ) ;
	memset( dis , 0x3f , sizeof(dis) ) ;
	ans.clear() ;
	dis[s] = 0 ;
	while( !q.empty() ) {
		P tmp = q.top() ;
		q.pop() ;
		int u = tmp.second ;
		vis[u] = 1 ;
		for( int i = head[u] ; ~i ; i = G[i].nxt ) {
			int v = G[i].v ;
			if( !vis[v] && dis[v] > dis[u] + G[i].w ) {
				dis[v] = dis[u] + G[i].w ;
				q.push(P(dis[v],v));
			}
		}
	}
	for( int j = 1 ; j <= n ; j++ ) {
		if( j != s && dis[j] < INF )
			ans.push_back( dis[j] ) ;
	}
	if( ans.size() < 2 ) return -1 ;
	sort( ans.begin() , ans.end() , cmp ) ;
	return ans[0] + ans[1] ;
}
int main() {
	int T ;
	scanf("%d",&T);
	while( T-- ) {
		tot = 0 ;
		int res = -1 ;
		memset( head , -1 , sizeof(head) ) ;
		memset( dis , 0x3f , sizeof(dis) ) ;
		scanf("%d%d",&n,&m);
		while( m-- ) {
			scanf("%d%d%d",&x,&y,&w);
			add( x , y , w ) ;
		}
		for( int i = 1 ; i <= n ; i++ ) {
			res = max( res , dijstra( i ) ) ;
		}
		printf("%d\n",res);
	}
	return 0 ;
}

最短路
思路:i到j路徑長度是 i ^ j ,這樣邊很多,可以將每條路徑分解成2的冪次長度相加,故每個點相連路徑只添加2的冪次長度,只有nlogn條邊。

#include <bits/stdc++.h>
using namespace std;
int n , m , c , s , t ;
const int AX = 2e5 + 66 ;
struct Node{
	int v , w ;
	Node( int v , int w ):v(v),w(w){}
	bool operator < ( const Node &a )const{
		return w > a.w ; 
	}
};
vector<Node>G[AX] ;
int dis[AX] ; 
int vis[AX] ; 
void dijstra(){
	priority_queue<Node>q ;
	q.push(Node(s,0));
	memset( dis , 0x3f , sizeof(dis) ) ;
	dis[s] = 0 ;
	while( !q.empty() ){
		Node tmp = q.top() ; 
		q.pop();
		vis[tmp.v] = 1 ; 
		for( int i = 0 ; i < G[tmp.v].size() ; i++ ){
			int to = G[tmp.v][i].v ; 
			if( !vis[to] && dis[to] > dis[tmp.v] + G[tmp.v][i].w ){
				dis[to] = dis[tmp.v] + G[tmp.v][i].w ;
				q.push(Node(to,dis[to]));
			}
		}
	}
}
int main() {
	scanf("%d%d%d",&n,&m,&c);
	int x , y , w ; 
	for( int i = 0 ; i < m ; i++ ) {
		scanf("%d%d%d",&x,&y,&w) ;
		G[x].push_back(Node(y,w));
	} 
	scanf("%d%d",&s,&t);
	int tot = 1 ; 
	while( tot <= n ) tot <<= 1 ; 
	for( int i = 1 ; i < tot ; i++ ){
		for( int p = 1 ; p < tot ; p <<= 1 ){
			G[i].push_back(Node(i^p,p*c));
		}
	}
	dijstra();
	printf("%d\n",dis[t]);
	return 0 ;
}



強連通分量:

這題跟求強連通似乎並無關係0.0
不過簡單題練練手
B題

思路:因爲是個環,解一定是從1順時針,或者逆時針。

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
const int AX = 1e2 + 66 ; 
int n ;
int a[AX][AX];
int res ; 
void dfs( int x , int pre , int num , int sum ){
	if( num == n + 1 ){
		res = min( res , sum );
		return ; 
	}
	for( int i = 1 ; i <= n ; i++ ){
		if( a[x][i] >= 0 && i != pre ){
			dfs( i , x , num + 1 , sum + a[x][i] );
		}
	}
}
int main() {
	int x , y , w ; 
	while( ~scanf("%d",&n) ){
		memset( a , -1 , sizeof(a) ) ;
		res = INF ; 
		for( int i = 0 ; i < n ; i++ ){
			scanf("%d%d%d",&x,&y,&w);
			a[y][x] = w ;
			a[x][y] = 0 ; 
		}
		dfs( 1 , -1 , 1 , 0 );
		printf("%d\n",res);
	}
	return 0 ;
}

二分圖匹配

Graph Coloring I

思路:
染色法判斷有無簡單奇環
有簡單奇環則染色不成功

輸出環那部分比較難受

#include <bits/stdc++.h>
using namespace std;
const int AX = 3e5 + 66 ;
int n , m ;
vector<int>odd;
int col[AX] ;
vector<int>G[AX];
int pre[AX] ;
int f , s ;
void dfs( int x , int val ) {
	if( f ) return ;
	col[x] = val ;
	for( int i = 0 ; i < (int)G[x].size() ; i ++ ) {
		int y = G[x][i] ;
		if( col[y] == -1 ) {
			pre[y] = x ;
			dfs( y , val ^ 1 );
		} else if( col[x] == col[y] ) { // 有奇數環
			f = y ;   // 環終點
			s = x ;   // 環起點
			return ;
		}
	}
	return ;
}
int main() {
	int x , y ;
	scanf("%d%d",&n,&m);
	f = 0 ; // 無奇環
	memset( col , -1 , sizeof(col) ) ;
	for( int i = 0 ; i < m ; i++ ) {
		scanf("%d%d",&x,&y);
		G[x].push_back(y);
		G[y].push_back(x);
	}
	dfs( 1 , 0 ) ;
	if( !f ) {
		printf("0\n");
		for( int i = 1 ; i <= n ; i++ ) {
			printf("%d ",col[i]);
		}
		printf("\n");
	} else {
		int k = 0 ;
        for( int i = f ; i != s ; i = pre[i] ){
            k ++ ;
        }
        printf("%d\n", k + 1 );
        for( int i = f ; i != s ; i = pre[i] ){
            printf("%d ",i);
        }
        printf("%d\n",s);
	}
	return 0 ;
}

字典樹

單詞查找樹
比板子題還要簡單些的一道,以前複試題也沒出過,就不寫了

#include <bits/stdc++.h>
using namespace std ;
struct Trie{
	struct Trie *nxt[26] ;
	char c ; 
};
Trie* create(){
	Trie* rt = new Trie() ; 
	memset( rt -> nxt , 0 , sizeof(rt->nxt) ) ;
	return rt ; 
}
void insert(  Trie* rt , char *s ){
	int len = strlen(s) ;
	Trie* t = rt ; 
	while( *s ){
		int id = (*s)-'A' ; 
		if( t -> nxt[id] == NULL ){
			t -> nxt[id] = create();
		}
		t = t -> nxt[id] ;
		s ++ ; 
	}
} 
int res ;
void dfs( Trie* rt ){
	res ++ ;
	for( int i = 0 ; i < 26 ; i++ ){
		if( rt -> nxt[i] ){
			dfs( rt -> nxt[i] );
		}
	}
}
int main(){
	char s[66];
	res = 0 ;
	Trie* rt = create() ;
	while( ~scanf("%s",s) ){
		insert( rt , s ) ;
	}
	dfs( rt );
	printf("%d\n",res);
	return 0 ; 
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章