特别有趣的一个题。
很容易想到可能是网络流问题,关键在于如何刻画诸如“Red边比Blue边多”这样的限制。
最后我还是看了题解。。。很有趣的思路。
对于每条边,假设她连接了左边点u和右边点v,那么:
- 从u到v连一条容量是1,费用是r的边,如果走了这条边,意味着这条边染Red。
- 从v到u连一条容量是1,费用是b的边,如果走了这条边,意味着这条边染Blue。
对于左边的有Red限制的点,显然要求这个点“出去的流量”大于“进来的流量”,因此从这个点连向T,下界是1,上界是INF,费用是0。
其他情况可以比葫芦画瓢推出如何连边,太简单了不写了。
所以是一个上下界的最小费用可行流。
#include <bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;
const int N = 210;
int _w;
namespace MCMF {
const int MAXN = 1000010;
const int MAXM = 1000010;
struct Edge {
int u, v, c, f, w;
Edge() {}
Edge( int uu, int vv, int cc, int ff, int ww ) {
u = uu, v = vv, c = cc, f = ff, w = ww;
}
};
int n, m, s, t;
int head[MAXN], nxt[MAXM];
Edge edge[MAXM];
void init( int nn ) {
n = nn, m = 0;
for( int i = 0; i < n; ++i )
head[i] = -1;
}
int adde( int u, int v, int c, int w ) {
int e = m;
edge[m] = Edge(u, v, c, 0, w);
nxt[m] = head[u], head[u] = m++;
edge[m] = Edge(v, u, 0, 0, -w);
nxt[m] = head[v], head[v] = m++;
return e;
}
int res[MAXN], from[MAXN];
int dis[MAXN];
bool inq[MAXN];
queue<int> q;
bool spfa() {
for( int i = 0; i < n; ++i )
dis[i] = 1e9;
dis[s] = 0, inq[s] = 1, q.push(s), res[s] = 1e9;
while( !q.empty() ) {
int u = q.front(); q.pop();
inq[u] = 0;
for( int i = head[u]; ~i; i = nxt[i] ) {
Edge &e = edge[i];
if( e.c > e.f && dis[u] + e.w < dis[e.v] ) {
dis[e.v] = dis[u] + e.w;
res[e.v] = min( res[u], e.c-e.f );
from[e.v] = i;
if( !inq[e.v] )
inq[e.v] = 1, q.push(e.v);
}
}
}
return dis[t] != 1e9;
}
void augment() {
int u = t, f = res[t];
while( u != s ) {
int i = from[u];
edge[i].f += f;
edge[i^1].f -= f;
u = edge[i].u;
}
}
pii solve( int ss, int tt ) {
s = ss, t = tt;
int flow = 0;
int cost = 0;
while( spfa() ) {
flow += res[t];
cost += res[t] * dis[t];
augment();
}
return pii(flow, cost);
}
}
int n1, n2, m, r, b;
char Lcolor[N], Rcolor[N], Ecolor[N];
pii edge[N];
int ans;
int S, T, SS, TT, nid, Lid[N], Rid[N];
int eid_red[N], eid_blue[N];
int add_edge( int u, int v, int l, int r, int w ) {
int e = MCMF::adde(u, v, r-l, w);
if( l ) {
MCMF::adde(SS, v, l, 0);
MCMF::adde(u, TT, l, 0);
}
return e;
}
bool solve() {
S = nid++, T = nid++, SS = nid++, TT = nid++;
for( int i = 1; i <= n1; ++i )
Lid[i] = nid++;
for( int i = 1; i <= n2; ++i )
Rid[i] = nid++;
MCMF::init(nid);
add_edge(T, S, 0, 1e9, 0);
int low_sum = 0;
for( int i = 1; i <= n1; ++i )
if( Lcolor[i] == 'R' ) {
add_edge(S, Lid[i], 1, 1e9, 0);
++low_sum;
} else if( Lcolor[i] == 'B' ) {
add_edge(Lid[i], T, 1, 1e9, 0);
++low_sum;
} else {
add_edge(S, Lid[i], 0, 1e9, 0);
add_edge(Lid[i], T, 0, 1e9, 0);
}
for( int i = 1; i <= n2; ++i )
if( Rcolor[i] == 'R' ) {
add_edge(Rid[i], T, 1, 1e9, 0);
++low_sum;
} else if( Rcolor[i] == 'B' ) {
add_edge(S, Rid[i], 1, 1e9, 0);
++low_sum;
} else {
add_edge(S, Rid[i], 0, 1e9, 0);
add_edge(Rid[i], T, 0, 1e9, 0);
}
for( int i = 1; i <= m; ++i ) {
int L = edge[i].first;
int R = edge[i].second;
eid_red[i] = add_edge(Lid[L], Rid[R], 0, 1, r);
eid_blue[i] = add_edge(Rid[R], Lid[L], 0, 1, b);
}
pii tmp = MCMF::solve(SS, TT);
if( tmp.first != low_sum ) return false;
ans = tmp.second;
for( int i = 1; i <= m; ++i ) {
using MCMF::edge;
int R = eid_red[i];
int B = eid_blue[i];
if( edge[R].f && !edge[B].f ) {
Ecolor[i] = 'R';
} else if( !edge[R].f && edge[B].f ) {
Ecolor[i] = 'B';
} else {
Ecolor[i] = 'U';
}
}
return true;
}
int main() {
_w = scanf( "%d%d%d%d%d", &n1, &n2, &m, &r, &b );
_w = scanf( "%s", Lcolor+1 );
_w = scanf( "%s", Rcolor+1 );
for( int i = 1; i <= m; ++i ) {
_w = scanf( "%d%d", &edge[i].first, &edge[i].second );
}
if( solve() ) {
printf( "%d\n", ans );
Ecolor[m+1] = 0;
puts(Ecolor + 1);
} else {
puts("-1");
}
return 0;
}
原文地址:https://www.cnblogs.com/mlystdcall/p/12246941.html
时间: 2024-10-07 12:43:49