美团2018校招真题题解 搜索 分层图最短路 前缀和(更新ing

病毒传播
思路:应该是比较简单的一道题,因为点数是千数量级的,floyd肯定不行了,看到边数也是1000,这就暗示了要从边下手,直接bfs找每个S集合内的点到各点的最短路这就是O(n*m),S不连通则无满足点,否则寻找S集合内满足以下条件的点:
对于S中的点x,到达S中除x外任意点距离 <= t && 到达G-S集合点距离 > t 的点x

思路不难,但是也卡了不少时间,只因为最后用auto k : mp 寻找S内满足条件的点之前,所有点x都被mp[x]这样访问过了,因此实际上auto k:mp 等价于for( int i = 1 ; i <= n ; i++ );再加上没有用本地IDE,调试也废了不少时间,记录下以前没注意的

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
const int AX = 1e3 + 6 ;
int n , m ; 
int k , t ;
vector<int>G[AX] ; 
map<int,int>mp ; 
vector<int>res ; 
int dis[AX][AX] ; 
struct Node{
    int u , d ;
    Node(){}
    Node( int u , int d ):u(u),d(d){}
} ;
int vis[AX] ; 
void bfs( int x ){
    queue<Node>q ; 
    q.push( Node( x , 0 ) ) ;
    vis[x] = 1 ; 
    while( !q.empty() ){
        Node k = q.front() ; q.pop() ; 
        int u = k.u ; 
        int d = k.d ;  
        for( int i = 0 ; i < (int)G[u].size() ; i++ ){
            int v = G[u][i] ; 
            if( !vis[v] ) { dis[x][v] = d + 1 ; q.push(Node(v,d+1)) ; vis[v] = 1 ;}
        }
    }
}
void solve(){
   for( auto i : mp ){
		memset( vis , 0 , sizeof(vis) ) ; 
		bfs( i.first ) ; 
   }
   for( int i = 1 ; i <= n; i++ ){ 
       if( !mp[i] ) continue ; 
       int j ;
       for( j = 1 ; j <= n ; j++ ){
           if( i == j ) continue ; 
           if(  mp[j] && ( dis[i][j] == -1 || dis[i][j] > t ) ) break ; 
           if( !mp[j] && dis[i][j] != -1 && dis[i][j] <= t ) break ; 
       } 
       if( j > n || ( i == n && j == n ) ) res.push_back(i) ;  
   }
}
int main(){
    int u , v ;
    cin >> n >> m ;
    memset( dis , -1 , sizeof(dis) ) ; 
    for( int i = 0 ; i < m ; i++ ){
        cin >> u >> v ; 
        if( u == v ) continue ; 
        G[u].push_back(v) ; 
        G[v].push_back(u) ; 
    }
    cin >> k >> t ;
    for( int i = 0 ; i < k ; i++ ){
        cin >> u ;
        mp[u] = 1 ; 
    }
    solve();
	int len = res.size() ; 
    if( !len ) cout << "-1" << endl;
    else{
        for( int i = 0 ; i < len ; i ++ ){
            cout << res[i] ;
            if( i != len - 1 ) cout << ' ' ; 
        }
    }
    return 0 ; 
}

公交车
思路:对于每个公交线路创建一个虚拟点,连接这条线路的所有点。对构造的图求1-n最短路,距离/2即为答案(因为多了到虚拟点的距离

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std ;
typedef pair<int,int>P ; 
const int AX = 1e5 + 666 ; 
struct Node{
	int v , w , nxt ;
	Node(){}
	Node( int v , int w , int nxt ):v(v),w(w),nxt(nxt){}
}G[AX<<1] ; 
int tot ; 
int head[AX] ; 
int vis[AX] ; 
int dis[AX] ; 
int n , m ;
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 ++  ; 
}
void djistra(){
	memset( dis , 0x3f , sizeof(dis) ) ; 
	priority_queue<P , vector<P> , greater<P> > q ;
	q.push(P(0,1)) ; 
	dis[1] = 0 ; 
	while( !q.empty() ){
		P tmp = q.top() ; 
		q.pop() ; 
		int u = tmp.second ; 
		int w = tmp.first ; 
		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 ) ) ;
			}
		}
	}
}
int main(){
	int x , y ; 
	memset( head , -1 , sizeof(head) ) ;
	memset( vis , 0 , sizeof(vis) ) ; 
	scanf("%d%d",&n,&m); 
	for( int i = 1 ; i <= m ; i++ ){
		scanf("%d",&x);
		while( x-- ){
			scanf("%d",&y);
			add( y , n + i , 1 );
		}
	}
	djistra();
	printf("%d\n",( dis[n] == INF ? -1 : dis[n] / 2 ) );
	return 0 ; 
}

共享单车
思路:分层图最短路,在最短路基础上加一维是否骑车即可。

#include <bits/stdc++.h>
#define LL long long
#define INF 0x3f3f3f3f3f3f3f3f
using namespace std;
const int AX = 1e5 + 666 ;
int n , m ;
int tot ;
int mark[AX] ;
int vis[5][AX] ;
struct Node {
	int v , w , nxt ;
	Node() {}
	Node( int v , int w , int nxt ):v(v),w(w),nxt(nxt) {}
} G[AX<<1];
struct RES {
	int u , sta ;
	LL w ;
	RES() {}
	RES( int u , int sta , LL w ):u(u),sta(sta),w(w) {}
	bool operator < ( const RES &a )const {
		return w > a.w ;
	}
};
int head[AX] ;
LL dis[5][AX] ;
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++ ;
}

void dijstra() {
	priority_queue<RES> q;
	if( mark[1] ) {
		q.push(RES(1,1,0)) ;
		dis[1][1] = 0 ;
	}
	q.push(RES(1,0,0)) ;
	dis[0][1] = 0 ;
	
	while( !q.empty() ) {
		RES tmp = q.top() ;
		q.pop() ;
		int u = tmp.u ;
		int sta = tmp.sta ;
		vis[sta][u] = 1 ;
		for( int i = head[u] ; ~i ; i = G[i].nxt ) {
			int v = G[i].v ;
			LL w = G[i].w ;
			if( sta == 1 && !vis[1][v] && dis[1][v] > dis[1][u] + (LL)(w / 2) ) {
				dis[1][v] = dis[1][u] + (LL)(w / 2) ;
				q.push(RES(v,1,dis[1][v]));
			}
			if( sta == 0 && !vis[0][v] && dis[0][v] > dis[0][u] + w ) {
				dis[0][v] = dis[0][u] + w ; 
				q.push(RES(v,0,dis[0][v]));
			}
			
			if( sta == 0 && mark[v] && !vis[1][v] && dis[1][v] > dis[0][u] + w ){
				dis[1][v] = dis[0][u] + w ; 
				q.push(RES(v,1,dis[1][v]));
			}
			
		}
	}
}

int main() {
	scanf("%d%d",&n,&m);
	int x , y , w , k ;
	memset( head , -1 , sizeof(head) ) ;
	memset( vis , 0 , sizeof(vis) );
	memset( mark , 0 , sizeof(mark) );
	memset( dis , 0x3f , sizeof(dis) );
	tot = 0 ;
	for( int i = 0 ; i < m ; i++ ) {
		scanf("%d%d%d",&x,&y,&w);
		if( x == y ) continue ;
		add( x , y , w ) ;
	}
	scanf("%d",&k);
	for( int i = 0 ; i < k ; i++ ) {
		scanf("%d",&x);
		mark[x] = 1 ;
	}
	dijstra();
	if( dis[0][n] == INF && dis[1][n] == INF ) printf("-1\n");
	else printf("%lld\n",min(dis[0][n],dis[1][n]));
	return 0 ;
}

重要节点
思路:图正反各存一遍,枚举每个点为起点,求能到达的点与能到自己的点的数量,比较大小。

#include <bits/stdc++.h>
using namespace std;
const int AX = 1e3 + 666 ;
int vis[AX] ;
vector<int>G[5][AX] ;
int ans[5] ;
void dfs( int x , int id ) {
	vis[x] = 1 ;
	ans[id] ++ ;
	for( int i = 0 ; i < G[id][x].size() ; i++ ) {
		int y = G[id][x][i] ;
		if( !vis[y] ) dfs( y , id ) ;
	}
}
int main() {
	int n , m ;
	scanf("%d%d",&n,&m);
	int x , y ;
	while( m-- ) {
		scanf("%d%d",&x,&y);
		if( x == y ) continue ;
		G[0][x].push_back(y) ;
		G[1][y].push_back(x) ;
	}
	int res = 0 ;
	for( int i = 1 ; i <= n ; i++ ) {
		ans[0] = 0 ;
		ans[1] = 0 ;
		memset( vis , 0 , sizeof(vis) ) ;
		dfs( i , 0 ) ;
		memset( vis , 0 , sizeof(vis) ) ;
		dfs( i , 1 ) ;
		if( ans[1] > ans[0] ) res ++ ;
	}
	printf("%d\n",res);
	return 0 ;
}

K的倍数
思路:求前缀和并对每个位置对k取余,余数相等的两个位置中间的和必能整除k。记录最长的

#include <bits/stdc++.h>
#define LL long long 
using namespace std;
const int AX = 1e5 + 66 ;
int a[AX] ;
LL sum[AX] ; 
int main(){
	int n ;
	scanf("%d",&n);
	sum[0] = 0 ; 
	for( int i = 1 ; i <= n ; i++ ){
		scanf("%d",&a[i]);
		sum[i] = sum[i-1] + a[i] ; 
	}
	int k ;
	scanf("%d",&k);
	map<int,int>mp;
	int res = 0 ; 
	for( int i = n ; i >= 1 ; i-- ){
		sum[i] %= k ;
		if( !sum[i] ) res = max( res , i ) ;
		if( mp[sum[i]] ) res = max( res , mp[sum[i]] - i );
		mp[sum[i]] = max( mp[sum[i]] , i ) ;
	}
	printf("%d\n",res) ;
	return 0 ;
}

友好城市

思路:floyd算法求得任意两点的最短路,然后dfs暴力求和。

#include <bits/stdc++.h>
#define LL long long
#define INF 0x3f3f3f3f3f3f3f3f
using namespace std;
const int AX = 1e2 + 66 ;
int n , K ;
LL dis[AX][AX] ;
vector<int>b;
LL res ;
void dfs( int i , LL sum ) {
	if( sum >= res ) return ;
	if( i > K ) return ;
	if( i == K ) {
		res = min( res , sum ) ;
		return ;
	}
	for( int j = i + 1 ; j < K ; j++ ) {
		swap( b[i+1] , b[j] ) ;
		dfs( i + 2 , sum + dis[b[i]][b[i+1]] ) ;
		swap( b[i+1] , b[j] ) ;
	}
}
int main() {
	scanf("%d",&n);
	int x ;
	res = INF ;
	for( int i = 1 ; i <= n ; i++ ) {
		for( int j = 1 ; j <= n ; j++ ) {
			scanf("%d",&x);
			dis[i][j] = ( ( x == -1 ) ? INF : x ) ;
			if( i == j ) dis[i][j] = 0 ;
		}
	}
	for( int k = 1 ; k <= n ; k++ ) {
		for( int i = 1 ; i <= n ; i++ ) {
			for( int j = 1 ; j <= n ; j++ ) {
				dis[i][j] = min( dis[i][j] , dis[i][k] + dis[k][j] );
			}
		}
	}
	scanf("%d",&K) ;
	K *= 2 ;
	for( int i = 0 ; i < K ; i++ ) {
		scanf("%d",&x);
		b.push_back(x) ;
	}
	dfs( 0 , 0 );
	printf("%lld\n",res);
	return 0 ;
}

猜你喜欢

转载自blog.csdn.net/FrankAx/article/details/105602505