题意
给你一个交叉路口,每一方向均有三个子方向,向左向前向右。每个转向方向上存在不同数量的车,每秒仅能通过一辆。在不相撞的情况下计算这些车通过路口的最小总时间。
分析
基本常识可以知道右转与其他不产生影响,考虑将右转单独进行计算,最后答案取 m a x max max。
将每个方向的左转和直行共8个方向进行标号,得到如下图和矩阵。(每个方向都优先标左,顺时针方向标号),其中-1表示右转。
手推样例可知,共有十二种组合可以同时进行,不会存在三种方向同时进行,最多两种方向同时进行, { { 1 , 2 } , { 1 , 5 } , { 1 , 8 } , { 2 , 3 } , { 2 , 6 } , { 3 , 4 } , { 3 , 7 } , { 4 , 8 } , { 4 , 5 } , { 6 , 5 } , { 6 , 7 } , { 7 , 8 } } ; \{\{1, 2\}, \{1, 5\}, \{1, 8\}, \{2, 3\}, \{2, 6\}, \{3, 4\},\{3, 7\}, \{4, 8\}, \{4, 5\}, \{6, 5\}, \{6, 7\}, \{7, 8\}\}; {{1,2},{1,5},{1,8},{2,3},{2,6},{3,4},{3,7},{4,8},{4,5},{6,5},{6,7},{7,8}};。意思是这两种同时向对应方向行进不会相撞。
考虑是否可以进行某种匹配,使得两个方向上可以同时行进的车数最多,每个方向上的剩余值再单独花费时间。答案=匹配数+未匹配数量。
考虑是否可以使用二分图匹最大权配??这里匹配出的值越多,代表同时进行的数量越多,答案越少。
将上述十二种组合作为十二条边,连成下图。
手推后发现不是二分图,看了题解才发现可以枚举两条边的权值,减去两条边后剩下的图构成二分图。
选择 { 1 , 8 } , { 4 , 5 } \{1,8\},\{4,5\} {1,8},{4,5}两条边进行枚举权值 v 1 , v 2 v1,v2 v1,v2。剩下的边先进行二分图染色,将点分成两部分, S S S向黑点 i i i连 v a l [ i ] val[i] val[i]的容量,白点 j j j向汇点 T T T连 v a l [ j ] val[j] val[j]的容量。剩余的边按照上述十二条边组成的图连容量为 i n f inf inf的边。
跑 d i n i c dinic dinic,得到最大流 m a x _ f l o w max\_flow max_flow,即为最大匹配数量。在残留网络中计算剩余流量 m a x _ n o f l o w max\_noflow max_noflow,即在二分图中为进行匹配的剩余数量。
答案为 a n s = m i n ( a n s , m a x _ f l o w + m a x _ n o f l o w + v 1 + v 2 ) ans=min(ans, max\_flow+max\_noflow+v1+v2) ans=min(ans,max_flow+max_noflow+v1+v2),即向右转的最大值为 r i g h t right right, a n s = m a x ( a n s , r i g h t ) ans = max(ans,right) ans=max(ans,right)。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
#define endl '\n'
ll gcd(ll a, ll b) { return b == 0 ? a : gcd(b, a % b); }
ll lcm(ll a, ll b) { return a / gcd(a, b) * b; }
void input() { freopen("in.txt", "r", stdin), freopen("out.txt", "w", stdout); }
const int N = 100, M = N * 2, inf = 1e8;
vector<pii> edge = {{1, 2}, {1, 5}, {1, 8}, {2, 3}, {2, 6}, {3, 4},
{3, 7}, {4, 8}, {4, 5}, {6, 5}, {6, 7}, {7, 8}};
int t, g[N][N];
int target[][5] = {{0, 0, 0, 0, 0},
{0, 0, 1, 2, -1},
{0, -1, 0, 3, 4},
{0, 6, -1, 0, 5},
{0, 7, 8, -1, 0}};
vector<int> G[10];
int color[10], vis[10], val[10];
int n, m, S, T;
int h[N], e[M], f[M], ne[M], idx;
int d[N], cur[N];
void add(int a, int b, int c) {
e[idx] = b, f[idx] = c, ne[idx] = h[a], h[a] = idx++;
e[idx] = a, f[idx] = 0, ne[idx] = h[b], h[b] = idx++;
}
bool bfs() {
memset(d, -1, sizeof(d));
queue<int> q;
q.push(S);
d[S] = 0, cur[S] = h[S];
while (q.size()) {
int t = q.front();
q.pop();
for (int i = h[t]; ~i; i = ne[i]) {
int j = e[i];
if (d[j] == -1 && f[i]) {
d[j] = d[t] + 1;
cur[j] = h[j];
if (j == T) return 1;
q.push(j);
}
}
}
return 0;
}
int dfs(int u, int limit) {
if (u == T) return limit;
int flow = 0;
for (int i = cur[u]; ~i && flow < limit; i = ne[i]) {
cur[u] = i;
int j = e[i];
if (d[j] == d[u] + 1 && f[i]) {
int t = dfs(j, min(f[i], limit - flow));
if (!t) d[j] = -1;
f[i] -= t, f[i ^ 1] += t, flow += t;
}
}
return flow;
}
int dinic() {
int res = 0, flow;
while (bfs())
while (flow = dfs(S, inf)) res += flow;
return res;
}
int noflow(){
int res = 0;
for(int i = 0; i < idx; i += 2){
if(e[i] == T || e[i^1] == T || e[i] == S || e[i^1] == S) res += f[i];
}
return res;
}
void cal(int u, int col) {
if (color[u] || vis[u]) return;
color[u] = col, vis[u] = 1;
int now = col == 1 ? 2 : 1;
for (auto i : G[u]) cal(i, now);
}
// 删1-8,和4-5
int main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
input();
for (auto it : edge) { // 建图
if (it.first == 1 && it.second == 8 || it.first == 4 && it.second == 5) continue;
G[it.first].push_back(it.second), G[it.second].push_back(it.first);
}
cal(1, 1); // 染色
cin >> t;
while (t--) {
int right = 0, res = inf;
for (int i = 1; i <= 4; i++)
for (int j = 1; j <= 4; j++){
int x; cin>>x;
if(target[i][j] == -1){
right = max(right, x);
continue;
}
val[target[i][j]] = x;
}
int vm1 = min(val[1], val[8]), vm2 = min(val[4], val[5]);
for(int v1 = 0; v1 <= vm1; v1++){ // 枚举第一个权值
for(int v2 = 0; v2 <= vm2; v2++){ // 枚举第二个权值
memset(h, -1, sizeof(h)), idx = 0; // 清图
S = N - 2, T = N - 1;
for(int i = 1; i <= 8; i++){
int v = val[i];
if(i == 1 || i == 8) v -= v1;
if(i == 4 || i == 5) v -= v2;
if(color[i] == 1) add(S, i, v);
else add(i, T, v);
}
for(auto it : edge){
int u = it.first, v = it.second;
if(u == 4 && v == 5 || u == 1 && v == 8) continue;
if(color[v] == 1) swap(u, v);
add(u, v, inf);
}
int max_flow = dinic();
int max_noflow = noflow(); // 计算残留网络
res = min(max_flow + max_noflow + v1 + v2, res);
}
}
res = max(res, right);
cout<<res<<endl;
}
return 0;
}