#C231116C. 简单图(graph)


#C231116C. 简单图(graph)

标签(Label)

  • 最短路算法
  • 观察+分析

前言(Front talk)

$\qquad$对每种情况进行枚举,分析,正解并不意味着就一定是所有情况的通解,也有可能是每种情况的解的总和

网址(Website)

题目详情 - 简单图(graph) - Super

题解 - 简单图(graph) - Super

样例

题目(Problem)

给一个 $n$ 个点 $m$ 条边的无向简单图,求 $1$ 到 $n$ 经过恰好 $k$ 条边的最短简单路。

对于所有数据,满足 $2\le n\le 10^6$;$1\le m, k\le 10^6$;$\min(n, m, k)\le 5$;$1\le w_i\le 10^8$ 。

题解(Solution)

补:简单路定义:没有重边,没有环,没有经过两次及以上的点。

对于 $n<k\ ||\ m<k$ ,答案一定为 $0$;

剩下的 $k$ 只有 $5$ 种,考虑分类讨论:

  1. $k==1$ 直接将点 $1$ 所有边枚举一遍,输出最小值即可。
  2. $k==2$ 将 $1$ 和 $n$ 的所有能通往的点枚举一遍,输出最小值即可。
  3. $k>=3$ 将 $1$ 和 $n$ 的所有能通往的点求出,直接暴力 $\verb!dfs!$ ,注意要记录不要走一个点两次,去掉直接连向 $1-n$ 的点,时间复杂度优于正解(至少快 $1$ 倍)。

代码(Code)

#include<bits/stdc++.h>
#include<vector>
#include<queue>
#define For(i,l,r) for(int i=l;i<=r;i++) 
#define Rof(i,l,r) for(int i=l;i>=r;i--)
using namespace std;
inline int input(){
	char c;bool f=false;while(!isdigit(c=getchar()))f=c=='-';int x=c^48;
	while(isdigit(c=getchar())){x=(((x<<2)+x)<<1)+(c^48);}return f?-x:x;
}
const int inf = 0x3f3f3f3f;
const int N = 1012345;
vector<pair<int,int> > ft[N];
int n,m,k,ans=inf,dis[N][2];

bitset<N> vst;
void dfs_ans(int x,int step,int sum){	
	if(sum>=ans) return;
	if(step==k) return ans=min(ans,sum+dis[x][1]),void();
	vst[x]=true;
	for(auto p:ft[x]){
		int y=p.first,v=p.second;
		if(vst[y]) continue;	
//		printf("%d %d %d %d %d\n",x,y,v,step,sum);
//		if(step+1==k) {ans=min(ans,sum+v+dis[y][1]);continue;}
		dfs_ans(y,step+1,sum+v);
	}vst[x]=false;
}
signed main(){
	string str("graph");
	freopen((str+".in").c_str(),"r",stdin);
	freopen((str+".out").c_str(),"w",stdout);
	memset(dis,0x3f,sizeof dis);
	n=input(),m=input(),k=input();
	For(i,1,m){
		int x=input(),y=input(),v=input();
		ft[x].emplace_back(y,v);
		ft[y].emplace_back(x,v);
	}if(k>5 || m<k || n<k) return printf("-1\n"),0;
	switch(k){
		case 1:{
			for(auto p:ft[1]){
				int y=p.first,v=p.second;
				if(y==n) ans=min(ans,v);
			}break;
		}
		case 2:{
			for(auto p:ft[1]){
				int y=p.first,v=p.second;
				dis[y][0]=min(dis[y][0],v);
			}for(auto p:ft[n]){
				int y=p.first,v=p.second;
				ans=min(dis[y][0]+v,ans);
			}break;
		}
		default:{
			set<int> s;
			for(auto p:ft[1]){
				int y=p.first,v=p.second;
				dis[y][0]=min(dis[y][0],v);
				if(y!=n) s.insert(y);
			}for(auto p:ft[n]){
				int y=p.first,v=p.second;
				dis[y][1]=min(dis[y][1],v);
			}vst[1]=vst[n]=true;
			for(auto x:s) dfs_ans(x,2,dis[x][0]);
			break;
		}
	}return printf("%d\n",ans>=inf?-1:ans),0;
}

后续(Ending)

$\qquad$其实我的代码时间复杂度和题解相像,但是其实 $dis$ 数组可以在输入的时候就可以处理好,时间更优,也不用搞一个 $set$ 来去重(主要是因为我懒),这样最快的有跑 $5000ms$ 的(当然加了快读),题解大概 $23000ms$ ,我的代码大概 $20000ms$ 。而且题解的处理方式还要更复杂一点。


文章作者: WolfDeer
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 WolfDeer !
  目录