《算法竞赛·快冲300题》将于2024年出版,是《算法竞赛》的辅助练习册。
所有题目放在自建的OJ New Online Judge。
用C/C++、Java、Python三种语言给出代码,以中低档题为主,适合入门、进阶。
“ 花费与流量” ,链接: http://oj.ecustacm.cn/problem.php?id=1717
题目描述
【题目描述】 给你一张n个点,m条边的无向图。第i条边需要花费ci元,同时流量为fi。
现在希望购买一条路径使得1到n连通。
路径花费X等于路径上所有边的花费。
路径流量Y等于路径上所有边的最小流量。
请最大化X与Y之比。。
【输入格式】 输入第一行包含n和m(2≦N≦1000,1≦M≦1000)
接下来m行,每行四个整数为u,v,c,f,分别表示边的两端点、花费和流量。(1≦c,f≦1000)。
【输出格式】 输出比值乘以1000000的值,向下取整。
【输入样例】
3 2
2 1 2 4
2 3 5 3
【输出样例】
428571
题解
题目所求的不是简单的最短路。每条边有2个属性:花费、流量。一条路径的总花费是路径上所有边的花费之和;而一条路径的流量等于路径上所有边中最小的流量。样例中,点1到点3的路径花费是2+5=7,流量是min(4,3)=3,比值是3*1000000/7=428571。
本题是“有流量约束的最短路径”。由于题目中的图不大,可以用暴力的方法找最大比值。
逐一遍历m个边的流量,对每个流量做一次dijkstra,也就是保留图上大于等于这个流量的边,然后求1到n的最短路。然后求在所有X与Y之比中,找最大的哪个。
做一次Dijkstra的复杂度是nlogn,一共做m次,总复杂度为mnlogn。
本题的Dijkstra代码基本上是抄了模板。如果不太了解Dijkstra,请看教材。
【重点】 掌握基本的Dijkstra代码。
C++代码
下面的代码用链式前向星存图。
dijkstra()和模板差不多,只是加上了流量约束。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int,int> PII;
const int N = 1010;
int n, m;
int head[N], e[N*2], nex[N*2], w[N*2], f[N*2], cnt; //链式前向星存图
int flow[N]; //记录所有的流
int dist[N]; //dist[i]: 起点到i的路径长度
bool done[N]; //done[i]=true: 到i的最短路已经找到
void add(int a,int b, int c, int d){ //用链式前向星存图
e[cnt] = b, w[cnt] = c, f[cnt] = d, nex[cnt] = head[a], head[a] = cnt++;
}
int dijkstra(int x){
memset(dist, 0x3f3f3f3f, sizeof dist);
memset(done, 0, sizeof done);
dist[1] = 0; //1到1的距离为0
priority_queue<PII,vector<PII>,greater<PII>> q;
q.push({0, 1});
while(!q.empty()){
auto t = q.top();
q.pop();
int d = t.first, u = t.second;
if(done[u]) continue; //丢弃已经找到最短路径的节点
done[u] = true;
for(int i = head[u];i != -1;i = nex[i]){ //检查u的所有邻居
int v = e[i];
if(f[i] >= x) //流量约束:只走流大于x的边
if(dist[v] > d + w[i]) {
dist[v] = d + w[i];
q.push({dist[v],v});
}
}
}
if(dist[n] != 0x3f3f3f3f) return dist[n]; //返回最短路径
return -1;
}
int main(){
cin>>n>>m;
memset(head, -1, sizeof head);
for(int i = 0;i < m;i ++) {
int u, v, w, f; cin>>u>>v>>w>>f;
add(u, v, w, f); //存边
add(v, u, w, f);
flow[i] = f; //把所有的流存在一个数组里
}
LL best_f = 0, best_w = 1, cur_w, cur_f;
for (int i = 0;i < m ;i ++) {
cur_f = flow[i];
cur_w = dijkstra(flow[i]); //图上只保留流大于flow[i]的边,然后找最短路径
if (cur_w != -1)
if (cur_f* best_w > best_f * cur_w){ //更新最大比
best_f = cur_f; best_w = cur_w;
}
}
cout << best_f * 1000000LL / best_w << endl;
return 0;
}
Java代码
import java.util.*;
import java.lang.*;
import java.io.*;
class Main {
static final int N = 1010;
static int[] head = new int[N];
static int[] e = new int[N*2];
static int[] nex = new int[N*2];
static int[] w = new int[N*2];
static int[] f = new int[N*2];
static int[] flow = new int[N];
static int[] dist = new int[N];
static boolean[] done = new boolean[N];
static int cnt;
static int n;
static void add(int a, int b, int c, int d) {
e[cnt] = b; w[cnt] = c;
f[cnt] = d; nex[cnt] = head[a];
head[a] = cnt++;
}
static int dijkstra(int x) {
Arrays.fill(dist, 0x3f3f3f3f);
Arrays.fill(done, false);
dist[1] = 0;
PriorityQueue<PII> q = new PriorityQueue<>(new Comparator<PII>() {
@Override
public int compare(PII o1, PII o2) {
return o1.x - o2.x;
}
});
q.offer(new PII(0, 1));
while (!q.isEmpty()) {
PII t = q.poll();
int d = t.x;
int u = t.y;
if (done[u]) continue;
done[u] = true;
for (int i = head[u]; i != -1; i = nex[i]) {
int v = e[i];
if (f[i] >= x)
if (dist[v] > d + w[i]) {
dist[v] = d + w[i];
q.offer(new PII(dist[v], v));
}
}
}
if (dist[n] != 0x3f3f3f3f) return dist[n];
return -1;
}
public static void main(String[] args) throws Exception {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
int m = sc.nextInt();
Arrays.fill(head, -1);
for (int i = 0; i < m; i++) {
int u = sc.nextInt();
int v = sc.nextInt();
int c = sc.nextInt();
int d = sc.nextInt();
add(u, v, c, d);
add(v, u, c, d);
flow[i] = d;
}
long best_f = 0, best_w = 1, cur_w, cur_f;
for (int i = 0; i < m; i++) {
cur_f = flow[i];
cur_w = dijkstra(flow[i]);
if (cur_w != -1)
if (cur_f * best_w > best_f * cur_w) {
best_f = cur_f;
best_w = cur_w;
}
}
System.out.println(best_f * 1000000L / best_w);
}
static class PII {
int x, y;
public PII(int x, int y) {
this.x = x;
this.y = y;
}
}
}
Python代码
import heapq
N = 1010
head = [-1] * N
e = [0] * (N * 2)
nex = [0] * (N * 2)
w = [0] * (N * 2)
f = [0] * (N * 2)
flow = [0] * N
dist = [0x3f3f3f3f] * N
done = [False] * N
cnt = 0
n = 0
def add(a, b, c, d):
global cnt
e[cnt] = b
w[cnt] = c
f[cnt] = d
nex[cnt] = head[a]
head[a] = cnt
cnt += 1
def dijkstra(x):
global dist, done, n
dist = [0x3f3f3f3f] * N
done = [False] * N
dist[1] = 0
q = []
heapq.heappush(q, (0, 1))
while q:
t = heapq.heappop(q)
d = t[0]
u = t[1]
if done[u]: continue
done[u] = True
i = head[u]
while i != -1:
v = e[i]
if f[i] >= x:
if dist[v] > d + w[i]:
dist[v] = d + w[i]
heapq.heappush(q, (dist[v], v))
i = nex[i]
if dist[n] != 0x3f3f3f3f: return dist[n]
return -1
if __name__ == '__main__':
n, m = map(int, input().split())
for i in range(m):
u, v, c, d = map(int, input().split())
add(u, v, c, d)
add(v, u, c, d)
flow[i] = d
best_f = 0
best_w = 1
cur_w = 0
cur_f = 0
for i in range(m):
cur_f = flow[i]
cur_w = dijkstra(flow[i])
if cur_w != -1:
if cur_f * best_w > best_f * cur_w:
best_f = cur_f
best_w = cur_w
print(best_f * 1000000 // best_w)