9.9 图论练习七模拟赛小记
A.环形跑
A.环形跑,原题:P3199 [HNOI2009] 最小圈
学习了一下 01 分数规划就会了。
题意:找到一条环,使得环上边权和除以节点个数最小,求该值。
显然这个值满足单调性,且两个参数直接也不好直接找答案,故二分找这样一个环来判断答案正确性。
则判断时要求是否存在这样一个环,满足:
移项得到:
因为
所以最后化简为:
于是就是判断图中有无负环。
然后 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.奶牛串门
赛时先开的这道,第一遍把 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 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步