ZOJ3229 Shoot the Bullet

嘟嘟嘟


有源有汇上下界网络流之最大流。


这题建图其实不难,从\(s\)向每一天连容量为\([0, D]\)的边,每一天向对应的女孩连容量为\([L, R]\)的边,每一个女孩向汇点连容量为\([G, INF]\)的边。
然后转换成上下界网络流的图:建立附加源附加汇\(S, T\),计算每一个点的出入度之差后连边。这时候还要从\(t\)到\(s\)连一条\([0, INF]\)的边,形成循环流。
然后从\(S\)到\(T\)跑一边最大流,如果流满。则说明存在可行流,于是切掉\(t\)到\(s\)的边,再从\(s\)到\(t\)跑一边最大流。则答案就是第一次\(s\)到\(t\)的流加上第二次的流。
求第一次\(s\)到\(t\)的流有一个技巧,因为已经连了\(t\)到\(s\)的一条边,所以只要取反向边的流量的相反数即可。


19.04.24更新
在某篇博客上看到,可以不删边,再跑一遍\(s, t\)的最大流,那么答案就是这次的最大流!


然后我因为数组开的过大(才$5e6$)在zoj上段错误了…… ```c++ #include #include #include #include #include #include #include #include #include #include using namespace std; #define enter puts("") #define space putchar(' ') #define Mem(a, x) memset(a, x, sizeof(a)) #define rg register typedef long long ll; typedef double db; const int INF = 0x3f3f3f3f; const db eps = 1e-8; const int maxn = 1505; const int maxe = 1e5 + 5; inline ll read() { ll ans = 0; char ch = getchar(), last = ' '; while(!isdigit(ch)) last = ch, ch = getchar(); while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar(); if(last == '-') ans = -ans; return ans; } inline void write(ll x) { if(x < 0) x = -x, putchar('-'); if(x >= 10) write(x / 10); putchar(x % 10 + '0'); }

int n, m, s, t, S, T;

struct Edge
{
int nxt, from, to, cap, flow;
}e[maxe];
int head[maxn], ecnt = -1;
void addEdge(int x, int y, int w)
{
e[++ecnt] = (Edge){head[x], x, y, w, 0};
head[x] = ecnt;
e[++ecnt] = (Edge){head[y], y, x, 0, 0};
head[y] = ecnt;
}

int dis[maxn];
bool bfs(int s, int t)
{
Mem(dis, 0); dis[s] = 1;
queue q; q.push(s);
while(!q.empty())
{
int now = q.front(); q.pop();
for(int i = head[now], v; i != -1; i = e[i].nxt)
{
if(!dis[v = e[i].to] && e[i].cap > e[i].flow)
{
dis[v] = dis[now] + 1;
q.push(v);
}
}
}
return dis[t];
}
int cur[maxn];
int dfs(int now, int _t, int res)
{
if(now == _t || res == 0) return res;
int flow = 0, f;
for(int& i = cur[now], v; i != -1; i = e[i].nxt)
{
if(dis[v = e[i].to] == dis[now] + 1 && (f = dfs(v, _t, min(res, e[i].cap - e[i].flow))) > 0)
{
e[i].flow += f; e[i ^ 1].flow -= f;
flow += f; res -= f;
if(res == 0) break;
}
}
return flow;
}

int maxflow(int s, int t)
{
int flow = 0;
while(bfs(s, t))
{
memcpy(cur, head, sizeof(head));
flow += dfs(s, t, INF);
}
return flow;
}

int b[maxn][maxn], d[maxn];
void init()
{
Mem(head, -1); ecnt = -1;
Mem(d, 0); Mem(b, 0);
s = 0, t = n + m + 1;
S = t + 1; T = S + 1;
}

int st[maxn];
void print()
{
for(int i = 1; i <= n; ++i)
{
int top = 0;
for(int j = head[i], v; j != -1; j = e[j].nxt)
{
v = e[j].to;
if(v > n && v <= n + m) //别忘了前向星是倒着存边
st[++top] = e[j].flow + b[i][v - n];
}
while(top) write(st[top]), enter, top--;
}
}

int main()
{
while(~scanf("%d%d", &n, &m))
{
init();
for(int i = 1; i <= m; ++i)
{
int x = read();
addEdge(i + n, t, INF - x);
d[i + n] += x; d[t] -= x;
}
for(int i = 1; i <= n; ++i)
{
int C = read(), D = read();
addEdge(s, i, D);
//d[s] += D; d[i] -= D; //最小流量为0,所以不加
for(int j = 1; j <= C; ++j)
{
int id = read() + 1, L = read(), R = read();
b[i][id] = L;
addEdge(i, id + n, R - L);
d[i] += L; d[id + n] -= L;
}
}
int tot = 0;
for(int i = 0; i <= t; ++i)
if(d[i] < 0) addEdge(S, i, -d[i]);
else addEdge(i, T, d[i]), tot += d[i];
addEdge(t, s, INF);
if(maxflow(S, T) < tot) {puts("-1\n"); continue;}
int tp = -e[ecnt].flow; e[ecnt].flow = e[ecnt].cap; // 相当于删去
//上面这一行可以不用,这样答案直接就是maxflow(s, t)
write(tp + maxflow(s, t)), enter;
print(); enter;
}
return 0;
}

上一篇:Flow 和 TypeScript 基础回顾


下一篇:layui flow 流加载 笔记