题意:起点和终点有限制的最小可重复路径覆盖。
思路1:上下界网络流,拆点,使得每个点的流量下界限制为1.(注意有源汇上下界网络流只能跑DAG)
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef int lint;
const lint inf = 0x3f3f3f3f;
const int maxn = 1005;
const int maxm = 20005;
struct dinic{
static const int N = 10005;
static const int M = 200005;
static const lint inf = 0x3f3f3f3f;
int he[N],ne[M],ver[M],d[N];
lint edge[M];
int s,t,tot;
queue<int> que;
void init( int n ){
for( int i = 0;i <= n;i++ ){
he[i] = 0;
}
tot = 1;
}
void add( int x,int y,lint z ){
ver[++tot] = y; ne[ tot ] = he[x]; he[x] = tot; edge[ tot ] = z;
ver[++tot] = x; ne[tot] = he[y]; he[y] = tot;edge[tot] = 0;
}
bool bfs(){
memset( d,0,sizeof( d ) );
while( que.size() ) que.pop();
que.push( s );
d[s] = 1;
while( que.size() ){
int x = que.front();
que.pop();
for( int cure = he[x];cure;cure = ne[cure] ){
int y = ver[cure];
if( edge[cure] && !d[y] ){
que.push( y );
d[ y ] = d[x] + 1;
if( y == t ) return 1;
}
}
}
return 0;
}
lint dfs( int x,lint flow ){
if( x== t ) return flow;
lint rest = flow,k;
for( int cure = he[x];cure && rest;cure = ne[cure] ){
int y = ver[cure];
if( edge[cure] && d[ y ] == d[x] + 1 ){
k = dfs( y,min( rest,edge[cure] ) );
if( !k ) d[ y ] = 0;
edge[cure] -= k;
edge[ cure^1 ] += k;
rest -= k;
}
}
return flow - rest;
}
lint max_flow( int x,int y ){
s = x; t = y;
lint maxflow = 0;
lint flow = 0;
while( bfs() )
while( flow = dfs( s,inf ) ) maxflow += flow;
return maxflow;
}
} g;
int ver[maxm],he[maxn],ne[maxm],tot;
int st[maxn],ins[maxn],c[maxn],dfn[maxn],low[maxn];
int num,top,cnt;
void add( int x,int y ){
ver[++tot] = y;
ne[tot] = he[x];
he[x] = tot;
}
void tarjan( int x ){
dfn[x] = low[x] = ++num;
st[++top] = x;ins[x] = 1;
for( int cure = he[x];cure;cure= ne[cure] ){
int y= ver[cure];
if(!dfn[y] ){
tarjan(y);
low[x] = min( low[x],low[y] );
}else if( ins[y] ){
low[x] = min( low[x],dfn[y] );
}
}
if( dfn[x] ==low[x] ){
cnt++;int y;
do{
y = st[top--],ins[y] = 0;
c[y] = cnt;
}while( x!=y );
}
}
vector<int> vea,veb;
void init(int n){
tot = 1;num = cnt = 0;
memset( dfn,0,sizeof(int)*(n+1) );
for( int i = 0; i<= n;i++ ) he[i] = 0;
}
int main(){
int ca;
scanf("%d",&ca);
while(ca--) {
int n, m, a, b;
scanf("%d%d%d%d", &n, &m, &a, &b);
init(n);vea.clear();veb.clear();
for (int x, i = 1; i <= a; i++) {
scanf("%d", &x);vea.push_back(x);
}
for (int x, i = 1; i <= b; i++) {
scanf("%d", &x);veb.push_back(x);
}
for (int x, y, i = 1; i <= m; i++) {
scanf("%d%d", &x, &y);
add(x,y);
}
for( int i = 1;i <= n;i++ )if(!dfn[i])tarjan(i);
int S = 0, T = 2 * cnt + 1, SS = T + 1, TT = SS + 1;
g.init(TT);
for( int i = 1; i <= cnt;i++ ){
g.add( i,i+cnt,inf );
g.add( i,TT,1 );
g.add( SS,i+cnt,1 );
}
for( int i = 0 ;i < vea.size();i++ ){
int x= vea[i];
g.add( S,c[x],inf );
}
for( int i = 0;i < veb.size();i++ ){
int x= veb[i];
g.add( c[x]+cnt,T,inf );
}
for( int x= 1;x <= n;x++ ){
for( int cure = he[x];cure;cure = ne[cure] ){
int y = ver[cure];
if( c[x] == c[y] ) continue;
g.add( c[x]+cnt,c[y],inf );
}
}
g.max_flow(SS,TT);
g.add( T,S,inf );
int ans = g.max_flow( SS,TT );
bool flag = true;
for( int cure = g.he[SS];cure;cure = g.ne[cure] ){
if( g.edge[cure] ){
flag= false;
break;
}
}
if(!flag){
puts("no solution");
}else{
printf("%d\n",ans);
}
}
return 0;
}
思路2:费用流
首先第一感觉这个题很像最小路径覆盖……但他并不是一个DAGDAG。所以我们自己动手丰衣足食把它缩成一个DAGDAG。
现在变成了一个多起点多终点的最小路径覆盖问题。我们首先把一个点拆成uu和u′u′,并且uu连向u′u′一条流量为11,费用为1的边。
然后原图的边就u′u′连向vv一条流量为INFINF,费用为00的边。
源点SS向图中的起点ss连流量为INFINF,费用为00的边,终点t′t′向图中的汇点EE连流量为INFINF,费用为00的边。
跑一边最大费用最大流。如果费用为点数那么说明每个点都经过了,答案为增广次数。
否则无解。
坑点:费用流跑负环会死循环,需要tarjan缩点先。这里跑的最大费用流增广到 > 0 时需要return
#include <bits/stdc++.h>
using namespace std;
typedef int lint;
typedef long long LL;
const int maxn = 2005;
const int maxm = 20005;
const int inf = 0x3f3f3f3f;
namespace MFMC {
struct EDGE {
int from, to, inv;
int cap, cost;
};
vector<vector<EDGE> > es;
void init(int n) {
for (auto& i : es)
i.resize(0);
es.resize(n+1);
}
void add(int u, int v, int cap, int cost) { // 如果需要修改 cost为LL
es[u].push_back(EDGE{u, v, (int)es[v].size(), cap, cost});
es[v].push_back(EDGE{v, u, (int)es[u].size() - 1, 0, -cost});
}
//O(VE)
template<typename DT>
void spfa(int s, DT dist[], EDGE* rec[]) {
queue<int> q;
static bool inq[maxn];
memset(dist, 0x3f, es.size() * sizeof(DT));
memset(inq, 0, es.size() * sizeof(bool));
memset(rec, 0, es.size() * sizeof(int));
dist[s] = 0;
q.push(s);
while (!q.empty()) {
s = q.front();
q.pop();
inq[s] = false;
for (auto& e : es[s]) {
if (0 == e.cap)
continue;
if (dist[e.to] > dist[s] + e.cost) {
dist[e.to] = dist[s] + e.cost;
rec[e.to] = &e;
if (!inq[e.to]) {
q.push(e.to);
inq[e.to] = true;
}
}
}
}
}
template<typename DT>
void dijkstra_pq(int s, DT dist[], EDGE* rec[]) {
priority_queue<pair<DT, int> > q;//-dist, vertex
memset(dist, 0x3f, es.size() * sizeof(DT));
memset(rec, 0, es.size() * sizeof(int));
dist[s] = 0;
q.push(make_pair(0, s));
while (!q.empty()) {
s = q.top().second;
DT c = -q.top().first;
q.pop();
if (c != dist[s]) continue;
for (auto& e : es[s]) {
if (0 == e.cap)
continue;
if (dist[e.to] > c + e.cost) {
dist[e.to] = c + e.cost;
rec[e.to] = &e;
q.push(make_pair(-dist[e.to], e.to));
}
}
}
}
//Need dijkstra_GRAPH_EDGES_PQ
//O(FE log(E)),F is the maximum flow
template<typename FT, typename CT>
void mfmc(int s, int t, FT &maxflow, CT &mincost) {
CT inf;
memset(&inf, 0x3f, sizeof(CT));
static CT dist[maxn];
static EDGE* rec_e[maxn];
maxflow = mincost = 0;
CT realdist = 0; //real distance from s to t
bool first = true;
while (true) {
if (first) {
spfa( s, dist, rec_e);
first = false;
} else {
spfa( s, dist, rec_e);
}
if (inf == dist[t])
break;
FT minF = numeric_limits<FT>::max();
for (auto e = rec_e[t]; e; e = rec_e[e->from])
minF = min(minF, (FT)e->cap);
realdist += dist[t];
if( realdist >= 0 ) return;
maxflow += minF;
mincost += minF * realdist;
for (auto e = rec_e[t]; e; e = rec_e[e->from]) {
e->cap -= minF;
es[e->to][e->inv].cap += minF;
}
for (auto& i : es)
for (auto& e : i)
e.cost += dist[e.from] - dist[e.to];
}
}
};
int ver[maxm],ne[maxm],he[maxn],dfn[maxn],low[maxn];
int st[maxn],ins[maxn],c[maxn];
int tot,num,top,cnt;
void add( int x,int y ){
ver[++tot] = y;
ne[tot] = he[x];
he[x] = tot;
}
void tarjan( int x ){
dfn[x] = low[x] = ++num;
st[++top] = x,ins[x] = 1;
for( int cure = he[x];cure;cure= ne[cure] ){
int y = ver[cure];
if(!dfn[y]){
tarjan(y);
low[x] = min( low[x],low[y] );
}else if( ins[y] ){
low[x] = min( low[x],dfn[y] );
}
}
if( dfn[x]==low[x] ){
cnt++;int y;
do{
y = st[top--],ins[y] = 0;
c[y] = cnt;
}while(x!=y);
}
}
void init(int n){
tot = 1;
num = cnt = 0;
memset( he,0,sizeof(int)*(n+1) );
memset(dfn,0,sizeof(int)*(n+1));
}
int A[maxn],B[maxn];
int main(){
int ca;
scanf("%d",&ca);
while(ca--){
int n,m,a,b;
scanf("%d%d%d%d",&n,&m,&a,&b);
init(n);
for( int x,i = 1; i <= a;i++ ){
scanf("%d",&A[i]);
}
for( int x,i = 1;i <= b;i++ ){
scanf("%d",&B[i]);
}
for( int x,y,i = 1; i <= m;i++ ){
scanf("%d%d",&x,&y);
add(x,y);
}
for( int i = 1; i<= n;i++ )if(!dfn[i])tarjan(i);
int S = 0,T = 2*cnt+1;
MFMC::init(T);
for( int i = 1 ;i <= cnt;i++ ){
MFMC::add( i,i+cnt,1,-1 );
MFMC::add( i,i+cnt,inf,0 );
}
for(int i=1;i<=n;++i){
for(int j=he[i];j;j=ne[j]){
int u=c[i],v=c[ver[j]];
if( u == v ) continue;
MFMC::add(u+cnt,v,inf,0);
}
}
for( int i = 1;i <= a;i++ ){
int x=A[i];
MFMC::add( S,c[x],inf,0 );
}
for( int i = 1;i <= b;i++ ){
int x= B[i];
MFMC::add( c[x]+cnt,T,inf,0 );
}
int maxflow,mincost;
MFMC::mfmc(S,T,maxflow,mincost);
if( -mincost < cnt ){
puts("no solution");
}
else printf("%d\n",maxflow);
}
return 0;
}