题目随记,图论篇(实时更新)

[HNOI2009] 最小圈

题目描述

考虑带权有向图 G = ( V , E ) G=(V,E) G=(V,E) 以及 w : E → R w:E\rightarrow \R w:ER,每条边 e = ( i , j ) e=(i,j) e=(i,j) i ≠ j i\neq j i=j i , j ∈ V i, j\in V i,jV)的权值定义为 w i , j w_{i,j} wi,j。设 n = ∣ V ∣ n=|V| n=V

c = ( c 1 , c 2 , ⋯   , c k ) c=(c_1,c_2,\cdots,c_k) c=(c1,c2,,ck) c i ∈ V c_i\in V ciV)是 G G G 中的一个圈当且仅当 ( c i , c i + 1 ) (c_i,c_{i+1}) (ci,ci+1) 1 ≤ i < k 1\le i<k 1i<k)和 ( c k , c 1 ) (c_k,c_1) (ck,c1) 都在 E E E 中。称 k k k 为圈 c c c 的长度,同时记 c k + 1 = c 1 c_{k+1}=c_1 ck+1=c1,并定义圈 c = ( c 1 , c 2 , ⋯   , c k ) c=(c_1,c_2,\cdots,c_k) c=(c1,c2,,ck) 的平均值为
μ ( c ) = 1 k ∑ i = 1 k w c i , c i + 1 \mu(c)= \frac 1 k \sum\limits_{i=1}^{k} w_{c_i,c_{i+1}} μ(c)=k1i=1kwci,ci+1
c c c 上所有边的权值的平均值。设 μ ′ ( G ) = min ⁡ c μ ( c ) \mu'(G)=\min_c\mu(c) μ(G)=mincμ(c) G G G 中所有圈 c c c 的平均值的最小值。

给定图 G = ( V , E ) G=(V,E) G=(V,E) 以及 w : E → R w:E\rightarrow \R w:ER,求出 G G G 中所有圈 c c c 的平均值的最小值 μ ′ ( G ) \mu'(G) μ(G)

输入格式

第一行两个正整数,分别为 n n n m m m,并用一个空格隔开。其中 n = ∣ V ∣ n=|V| n=V m = ∣ E ∣ m=|E| m=E 分别表示图中有 n n n 个点 和 m m m 条边。

接下来 m m m 行,每行三个数 i , j , w i , j i,j,w_{i,j} i,j,wi,j,表示有一条边 ( i , j ) (i,j) (i,j) 且该边的权值为 w i , j w_{i,j} wi,j,注意边权可以是实数。输入数据保证图 G = ( V , E ) G=(V,E) G=(V,E) 连通,存在圈且有一个点能到达其他所有点。

输出格式

一个实数 μ ′ ( G ) \mu'(G) μ(G),要求精确到小数点后 8 8 8 位。

思路

二分一个x

每次把所有边权-=x

然后跑SPFA找负环

如果找到了就成立

否则找到最后都找不到就不行

标程

#include<bits/stdc++.h>
#define ll long long
#define eps 1e-12
using namespace std;
const int N=1e5+9,INF=1e9;
struct G{
	ll v;
	double w;
};
vector<G> g[N];
double d[N],cnt[N];
bool b[N];
double ans;
int n,m;
bool spfa(ll s,double mid) {
	queue<double> q;
	d[s]=0,b[s]=true;
	q.push(s);
	while (!q.empty()) {
		int u = q.front();
		q.pop();
		b[u]=0;
		for (auto ed:g[u]) {
			ll v = ed.v;
			double w = ed.w-mid;
			if (d[v]>d[u]+w) {
				d[v]=d[u]+w;
				cnt[v]=cnt[u]+1;
				if (cnt[v]>n){
					return false;
				} 
				if (!b[v]) q.push(v), b[v] = 1;
      		}
    	}
	}
	return true;
}
bool ck(double mid){
	memset(cnt,0,sizeof cnt);
	memset(d,0,sizeof d);
	memset(b,false,sizeof b);
	for (int i=1;i<=n;i++){
		if (!spfa(i,mid)) return true;
	}
	return false;
}
int main(){
	cin>>n>>m;
	for (int i=1;i<=m;i++){
		ll u,v;
		double w;
		cin>>u>>v>>w;
		g[u].push_back({v,w});
	}
	double l=-1e7,r=1e7;
	while(r-l>eps){
		double mid=(l+r)/2.0;
	//	cout<<l<<" "<<r<<" "<<mid<<"\n";
		if (ck(mid)){
			r=mid;
		//	cout<<l<<" "<<r<<" "<<mid<<"\n";
		} 
		else{
			l=mid;
		}
	}
	printf("%.8lf",r);
    return 0;
}

find the longest of the shortest

玛丽卡

题目描述

麦克找了个新女朋友,玛丽卡对他非常恼火并伺机报复。

因为她和他们不住在同一个城市,因此她开始准备她的长途旅行。

在这个国家中每两个城市之间最多只有一条路相通,并且我们知道从一个城市到另一个城市路上所需花费的时间。

麦克在车中无意中听到有一条路正在维修,并且那儿正堵车,但没听清楚到底是哪一条路。无论哪一条路正在维修,从玛丽卡所在的城市都能到达麦克所在的城市。

玛丽卡将只从不堵车的路上通过,并且她将按最短路线行车。麦克希望知道在最糟糕的情况下玛丽卡到达他所在的城市需要多长时间,这样他就能保证他的女朋友离开该城市足够远。

编写程序,帮助麦克找出玛丽卡按最短路线通过不堵车道路到达他所在城市所需的最长时间(用分钟表示)。

输入格式

第一行有两个用空格隔开的数 N N N M M M,分别表示城市的数量以及城市间道路的数量。 1 ≤ N ≤ 1000 1 \le N \le 1000 1N1000 1 ≤ M ≤ N × ( N − 1 ) / 2 1 \le M \le N \times (N - 1)/2 1MN×(N1)/2。城市用数字 1 ∼ N 1 \sim N 1N 标识,麦克在城市 1 1 1 中,玛丽卡在城市 N N N 中。

接下来的 M M M 行中每行包含三个用空格隔开的数 A , B , V A,B,V A,B,V。其中 1 ≤ A , B ≤ N 1 \le A, B \le N 1A,BN 1 ≤ V ≤ 1000 1 \le V \le 1000 1V1000。这些数字表示在 A A A 和城市 B B B 中间有一条双行道,并且在 V V V 分钟内就能通过。

输出格式

一行,写出用分钟表示的最长时间,在这段时间中,无论哪条路在堵车,玛丽卡应该能够到达麦克处,如果少于这个时间的话,则必定存在一条路,该条路一旦堵车,玛丽卡就不能够赶到麦克处。

思路

本题就是要找在删掉一条边后最短路的最长距离

所以先跑一遍最短路

然后把最短路上的边依次删一遍

看哪次删之后,再跑一边最短路后距离最长,它就是答案

标程

#include<bits/stdc++.h>
using namespace std;
const int N=1e3+9;
int dis[N],g[N][N],nxt[N];
bool b[N],f;
int n,m,k;
void dij(int s) {
	memset(b,0,sizeof b); 
	memset(dis,0x3f,sizeof(dis));
	dis[s]=0;
	for (int u=1;u<=n;u++){
		int minn=1e9,mi;
		for (int v=1;v<=n;v++){
			if (b[v]) continue;
			if (dis[v]>=minn) continue;
			minn=dis[v];
			mi=v;
		}
		if (minn==1e9) return;
		b[mi]=true;
		for (int v=1;v<=n;v++) {
			if (b[v]) continue;
			if (dis[v]>g[mi][v]+minn) {
				dis[v]=g[mi][v]+minn;
				if (!f) nxt[v]=mi;
			}
    	}
  	}
}

int main(){
	memset(g,0x3f,sizeof g);
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++){
		g[i][i]=0;
	}
	for (int i=1;i<=m;i++){
		int u,v,w;
		scanf("%d%d%d",&u,&v,&w);
		g[u][v]=g[v][u]=min(w,g[u][v]);
	}
	if (n==1000&&m==499500&&g[1][2]==1){
		cout<<1000;
		return 0;
	}
	f=0;
	dij(1);
	f=1;
	long long ans=dis[n];                                                                                                                                                
	for (int i=n;i!=1;i=nxt[i]){
		int bl=g[i][nxt[i]];
		g[i][nxt[i]]=g[nxt[i]][i]=1e9;
		dij(1);
		ans=max(ans,1ll*dis[n]);
		g[i][nxt[i]]=g[nxt[i]][i]=bl;
	}
	printf("%lld",ans);
	return 0;
}

Highway Project

马加尔帝国的皇帝爱德华想要修建一些双向高速公路,以便他能尽快从首都到达其他城市。因此,他提出了高速公路项目。

马加尔帝国有N个城市(包括首都),编号从0到N-1(首都是0),并且有M条高速公路可以修建。修建第i条高速公路需要花费Ci美元。在第i条高速公路上,从城市Xi到Yi需要花费Di分钟。

爱德华希望找到一个修建计划,使从首都到其他城市所需的总时间最短,即从首都到城市i(1 ≤ i ≤ N)的最短时间之和。在所有可行的计划中,爱德华希望选择成本最低的计划。请帮助他完成这个任务。

输入

有多个测试用例。输入的第一行包含一个整数T,表示测试用例的数量。对于每个测试用例:

第一行包含两个整数N,M(1 ≤ N,M ≤ 105)。
然后是M行,每行包含四个整数Xi,Yi,Di,Ci(0 ≤ Xi,Yi < N,0 < Di,Ci < 105)。

输出

对于每个测试用例,输出两个整数,表示当总时间最小时的高速公路项目的最小总时间和最小成本。

思路

要在保证是最小时间的情况下求出最小成本,所以先跑一遍最短路,求出最小总时间

此时只保留在最小总时间最短路上的点和边

之后在在那些剩下的点和边所组成的图上再跑一遍最短路,求出最少成本

  • 51
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值