曾记否,到中流击水,浪遏飞舟。|

Moyyer_suiy

园龄:2年8个月粉丝:4关注:18

9.9 图论练习七模拟赛小记

A.环形跑

原题:P3199 [HNOI2009] 最小圈

A.环形跑,原题:P3199 [HNOI2009] 最小圈

学习了一下 01 分数规划就会了。

题意:找到一条环,使得环上边权和除以节点个数最小,求该值。

显然这个值满足单调性,且两个参数直接也不好直接找答案,故二分找这样一个环来判断答案正确性。

则判断时要求是否存在这样一个环,满足:

i=1ka[wi]i=1kb[vi]<mid

移项得到:

i=1n(a[ei]mid×b[vi])<0

因为 b[vi]=1

所以最后化简为:

i=1n(a[ei]mid)<0

于是就是判断图中有无负环。

然后 spfa 判负环时写 bfs 也行,会麻烦些。dfs 的话,若这个点更新过则说明是负环,或一直递归往下判断。

Code P3199
#include<bits/stdc++.h>
typedef double db;
using namespace std;

const db eps=1e-9;
const int N=2e4+10;

int n,m;
int vis[N];
db w[N],dis[N],ans;
int idx,e[N],nxt[N],head[N];

void add(int x,int y,db z){
	e[++idx]=y;
	w[idx]=z;
	nxt[idx]=head[x];
	head[x]=idx;
}

bool spfa(int x,db v){
	vis[x]=1;
	for(int i=head[x];i;i=nxt[i])
		if(dis[e[i]]>dis[x]+w[i]-v){
			dis[e[i]]=dis[x]+w[i]-v;
			if(vis[e[i]]||spfa(e[i],v)) return 1;
		}
	vis[x]=0;
	return 0;
}

bool check(db x){
	for(int i=1;i<=n;i++) vis[i]=0,dis[i]=0;
	for(int i=1;i<=n;i++) if(spfa(i,x)) return 1;
	return 0;
}

int main(){
	scanf("%d%d",&n,&m);
	while(m--){
		int x,y;db z;
		scanf("%d%d%lf",&x,&y,&z);
		add(x,y,z);
	}
	db l=-1e7+2,r=1e7+2;
	while(r-l>=eps){
		db mid=(l+r)/2.000;
		if(check(mid)){
			ans=mid;
			r=mid-eps;
		}
		else l=mid+eps;
	}
	printf("%.8lf",ans);
}

B.奶牛串门

原题:P5764 [CQOI2005] 新年好

赛时先开的这道,第一遍把 add(y,x,z) 写成 add(z,y,x)调了半个小时。后来又检查发现第一遍开的数组大小是什么玩意啊。debug 能力为 0 以及怎么感觉没带脑子呢。

我的做法是以每个点为起点跑最短路,求出两个位置的最小距离。然后发现只有 5 个位置,5!= 120 还是能接受的。所以确定跑的顺序时可以直接用 dfs,next_permutationg 全排列也行。

Code P5764
#include<bits/stdc++.h>
using namespace std;

const int N=5e4+10;
const int M=2e5+10;
const int inf=0x3f3f3f3f;

int n,m;
int a[6],to[6][M];
int d[N],vis[M];
int idx,e[M],w[M],head[N],nxt[M];
int ans=0x7fffffff,v[6];

void add(int x,int y,int z){
	e[++idx]=y;
	w[idx]=z;
	nxt[idx]=head[x];
	head[x]=idx;
}

void dij(int s){
	priority_queue<pair<int,int> > q;
	memset(d,inf,sizeof d);
	memset(vis,0,sizeof vis);
	d[s]=0;
	q.push(make_pair(0,s));
	while(q.size()){
		int x=q.top().second;
		q.pop();
		if(vis[x]) continue;
		vis[x]=1;
		for(int i=head[x];i;i=nxt[i]){
			if(d[e[i]]>d[x]+w[i]){
				d[e[i]]=d[x]+w[i];
				q.push(make_pair(-d[e[i]],e[i]));
			}
		}
	}
}

void dfs(int x,int now,int tot){
	if(x==5){
		ans=min(ans,tot);
		return;
	}
	if(tot>ans) return;
	for(int i=1;i<=5;i++){
		if(!v[i]){
			v[i]=1;
			dfs(x+1,i,tot+to[now][a[i]]);
			v[i]=0;
		}
	}
}

int main(){
	scanf("%d%d",&n,&m);
	a[0]=1;
	for(int i=1;i<=5;i++) scanf("%d",&a[i]);
	while(m--){
		int x,y,z;
		scanf("%d%d%d",&x,&y,&z);
		add(x,y,z);
		add(y,x,z);
	}
	
	for(int i=0;i<=5;i++){
		dij(a[i]);
		for(int j=0;j<=5;j++){
			to[i][a[j]]=d[a[j]];
		}
	}
	
	dfs(0,0,0);
	printf("%d",ans);
}

C.阿龙打工

原题:P1938 [USACO09NOV] Job Hunt S

看数据范围感觉 floyd 非常可行。

初始时,如果这两条边之间通行不需要代价那么价值即给你的那个 l。否则减去代价。floyd 求最大价值,然后再加上一个 l (在起点获得的价值)。

最后判一下 -1,若一个点与起点构成了环且价值 > 2*l:跑这个环反而能多赚,所以最后无穷大。

Code P1938
#include<bits/stdc++.h>
using namespace std;

const int N=510;
const int inf=0x3f3f3f3f;

int l,n,m,t,s;
int ans;
int d[N][N];

int main(){
    scanf("%d%d%d%d%d",&l,&m,&n,&t,&s);
    for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) d[i][j]=(i==j)?0:(-inf);
    while(m--){
        int x,y;
		scanf("%d%d",&x,&y);
        d[x][y]=l;
    }
    while(t--){
        int x,y,z;
		scanf("%d%d%d",&x,&y,&z);
        d[x][y]=max(d[x][y],l-z);
    }
    for(int k=1;k<=n;k++)
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                d[i][j]=max(d[i][j],d[i][k]+d[k][j]);
    for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) d[i][j]+=l;
    
    for(int i=1;i<=n;i++)
        if(d[s][i]+d[i][s]>2*l&&i!=s) {puts("-1");return 0;}
    for(int i=1;i<=n;i++) ans=max(ans,d[s][i]);
    printf("%d",ans);
}

D.奶牛开车

原题:P1266 速度限制

思路来自机房大佬 xhx!%%%,非常感谢!!

多维状态,考虑用一个背包 dp 的思想,跑二维最短路。有点类似于 9.7 日模拟赛的 t2。

dis[i][j] 表示到达 i 号路,速度为 j 时的最小时间。

易证,如果每条路有限速,你的速度即为这条路的限速。

提前标记每条路速度,这样转移到下一条路限速为 0 时便为它的速度。以此之前速度为根据计算时间来跑最短路。

过程中标记一下,ans[i][j] 表示当到 i 号路时这条路速度为 j 时指向的路的编号和位置。dfs 找一下顺序即可。

这个是洛谷的码。oj 上有一点点的不同。

Code P1266
#include<iostream>
#include<cstdio>
#include<queue>
typedef double db;
using namespace std;

const int N=510;
const int M=1e5+10;
const db inf=10000000.0;

int n,m,s,en;
db dis[N][N];
int vis[N][N];
int idx,e[M],w[M],nxt[M],head[N],v[M];
struct node{int pos,lv;}ans[N][N];
struct que{
	int id,v;
	db d;
	bool operator <(const que x) const{
		return d>x.d;
	}
};
priority_queue<que> q;

void add(int x,int y,int z,int t){
	e[++idx]=y;
	w[idx]=t;
	v[idx]=z;
	nxt[idx]=head[x];
	head[x]=idx;
}

void dij(){
	for(int i=1;i<=n;i++) for(int j=1;j<=500;j++) dis[i][j]=inf;
	dis[0][70]=0;
	q.push((que){0,70,0});
	while(q.size()){
		int x=q.top().id,la=q.top().v;
		q.pop();
		if(vis[x][la]) continue;
		vis[x][la]=1;
		for(int i=head[x];i;i=nxt[i]){
			int nowv=v[i]; 
			if(!nowv) nowv=la;
			if(dis[e[i]][nowv]>dis[x][la]+1.0*w[i]/nowv){
				dis[e[i]][nowv]=dis[x][la]+1.0*w[i]/nowv;
				ans[e[i]][nowv]=(node){x,la};
				q.push((que){e[i],nowv,dis[e[i]][nowv]});
			}
		}
	}
}

void dfs(int now,int lst){
	if(now==0){
		printf("%d ",now);
		return;
	}
	dfs(ans[now][lst].pos,ans[now][lst].lv);
	printf("%d ",now);
}

int main(){
	scanf("%d%d%d",&n,&m,&en);
	while(m--){
		int x,y,z,t;
		scanf("%d%d%d%d",&x,&y,&z,&t);
		add(x,y,z,t);
	}
	dij();
	db minn=inf;
	int j=0;
	for(int i=1;i<=500;i++){
		if(minn>dis[en][i]){
			minn=dis[en][i];
			j=i;
		}
	}
	dfs(en,j);
	return 0;
}

我菜菜。菜菜。差评,差评!!

本文作者:Moyyer_suiy

本文链接:https://www.cnblogs.com/Moyyer-suiy/p/17689580.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   Moyyer_suiy  阅读(23)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起