牛客九 J-Jam
题意大概是一个十字路口,从每一个方向来的车会驶入其余的三个方向,总共有12种方向。给定每个方向车流的数量 c i , j c_{i,j} ci,j,问需要多少的单位时间可以使所有车辆驶过这个路口,并且要满足车流不能交错(如不能一边W->E一边N->S)。
数据范围: 0 < = c i , j < = 100 0<=c_{i,j}<=100 0<=ci,j<=100
题意不是很好理解,但是如果平时对于十字路口的信号灯有观察的话还是会大致明白这个题需要做什么。
在剔除了所有的右转之后,剩下八种车流,他们互相有着约束的关系,最后确定可以有十二种方案可以使两种车流同时通过。在比赛的时候,想的是一个整数规划,也就是以方案为变量,总共十二个变量,八个约束,但是整数规划不会,不知道怎么搞(或许可以先用单纯性然后找附近整点?)。乱搞了一个蒙特卡洛但是范围太大很难随机对,不是WA就是TLE。
打完后得知是用枚举的方式删掉两条边之后得到一个二分图,再跑网络流。
大概就是上面的这种图,可以发现,如果删掉7-8和3-4两条边,那么可以将左右两边分开,形成一个二分图。先枚举删掉的两条边的流量,将7,8两点和3,4两点分别减去之后,连接源点和汇点跑网络流,最后的最大流和之前枚举的两条边之和就是总共的可以两个方向的车辆同时通过的时间,用总的车流量去减,求最小值即为答案。
#include <bits/stdc++.h>
using namespace std;
const int N = 5, inf = 1e9;
int flow[N][N], b[N << 1], a[N << 2], num;
int head[20], s, t, dep[20], now[20];
struct edge
{
int nxt, to, flo;
};
edge e[50];
void addEdge(int x, int y, int z)
{
e[++ num].nxt = head[x];
head[x] = num;
e[num].to = y;
e[num].flo = z;
e[++ num].nxt = head[y];
head[y] = num;
e[num].to = x;
e[num].flo = 0;
}
bool bfs()
{
memset (dep, -1, sizeof (dep));
dep[s] = 0;
queue <int> q;
q.push(s);
while (!q.empty())
{
int x = q.front(); q.pop();
for (int i = head[x]; i != -1; i = e[i].nxt)
{
int to = e[i].to;
if (dep[to] == -1 && e[i].flo > 0)
{
dep[to] = dep[x] + 1;
q.push(to);
}
}
}
if (dep[t] == -1)
return false;
return true;
}
int dfs(int x, int f)
{
if (x == t)
return f;
for (int i = now[x]; i != -1; i = e[i].nxt)
{
now[x] = i;
int to = e[i].to;
if (e[i].flo > 0 && dep[to] == dep[x] + 1)
{
int val = dfs(to, min(f, e[i].flo));
if (val > 0)
{
e[i].flo -= val;
e[i ^ 1].flo += val;
return val;
}
}
}
return 0;
}
int dinic(int x, int y)
{
int ans = 0, val;
s = x; t = y;
while(bfs())
{
for (int i = 0; i <= 9; i ++)
now[i] = head[i];
while (val = dfs(s, inf))
{
ans += val;
}
}
return ans;
}
int main()
{
//freopen("in.txt", "r", stdin);
ios::sync_with_stdio(false);
cin.tie(0);
srand(time(0));
int T = 1;
cin >> T;
while (T --)
{
for (int i = 1; i <= 4; i ++)
for (int j = 1; j <= 4; j ++)
cin >> flow[i][j];
b[1] = flow[1][3]; b[2] = flow[1][2];
b[3] = flow[2][4]; b[4] = flow[2][3];
b[5] = flow[3][1]; b[6] = flow[3][4];
b[7] = flow[4][2]; b[8] = flow[4][1];
int ans = inf, ans1 = max(max(flow[1][4], flow[2][1]), max(flow[3][2], flow[4][3]));
int sum = 0;
for (int i = 1; i <= 8; i ++)
sum += b[i];
for (int i = 0; i <= min(b[7], b[8]); i ++)
for (int j = 0; j <= min(b[3], b[4]); j ++)
{
memset (head, -1, sizeof (head));
num = -1;
addEdge(0, 1, b[1]);
addEdge(0, 6, b[6]);
addEdge(0, 7, b[7] - i);
addEdge(0, 8, b[8] - i);
addEdge(2, 9, b[2]);
addEdge(5, 9, b[5]);
addEdge(3, 9, b[3] - j);
addEdge(4, 9, b[4] - j);
addEdge(1, 2, inf);
addEdge(1, 4, inf);
addEdge(1, 5, inf);
addEdge(6, 2, inf);
addEdge(6, 3, inf);
addEdge(6, 5, inf);
addEdge(7, 2, inf);
addEdge(7, 3, inf);
addEdge(8, 4, inf);
addEdge(8, 5, inf);
int val = dinic(0, 9);
val += i + j;
ans = min(ans, sum - val);
}
cout << max(ans, ans1) << "\n";
}
return 0;
}