最小不可交路径覆盖
题目链接:https://www.luogu.org/problemnew/show/P2764
题解:
如何建模?
把每个点i拆成xi和yi两个点。若i与j间有边,就链接xi与yj,求两个集合的最大匹配。
证明:
我们可以把一开始的点每个点看做一条路径。那么每增加一条匹配边,就相当于把两个路径连在一起。连起来的路径越多,则最后路径总数越少。
怎么求最大匹配?
网络流。
怎么输出方案?
一开始建两层图。比较修改的图与原图,有修改的地方就是连接的路径。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 #include<queue> 7 #include<bitset> 8 #define LL long long 9 #define RI register int 10 using namespace std; 11 const int INF = 0x7ffffff ; 12 const int N = 150 + 10 ; 13 const int M = 6000 + 10 ; 14 15 inline int read() { 16 int k = 0 , f = 1 ; char c = getchar() ; 17 for( ; !isdigit(c) ; c = getchar()) 18 if(c == ‘-‘) f = -1 ; 19 for( ; isdigit(c) ; c = getchar()) 20 k = k*10 + c-‘0‘ ; 21 return k*f ; 22 } 23 struct Edge { 24 int to, next, flow ; 25 }e[(M<<1)+(N<<2)], ee[(M<<1)+(N<<2)] ; 26 int n, m, s, t ; int head[N<<1], dep[N<<1] ; 27 bitset<N<<1>vis ; 28 inline void add_edge(int x,int y,int ff) { 29 static int cnt = 1 ; 30 e[++cnt].to = y, e[cnt].next = head[x], e[cnt].flow = ff, head[x] = cnt ; 31 ee[cnt].to = y, ee[cnt].next = head[x], ee[cnt].flow = ff; 32 e[++cnt].to = x, e[cnt].next = head[y], e[cnt].flow = 0, head[y] = cnt ; 33 ee[cnt].to = x, ee[cnt].next = head[y], ee[cnt].flow = 0 ; 34 } 35 36 inline bool F_bfs() { 37 memset(dep,0,sizeof(dep)) ; 38 queue<int>q ; q.push(s) ; dep[s] = 1 ; 39 while(!q.empty()) { 40 int x = q.front() ; q.pop() ; 41 for(int i=head[x];i;i=e[i].next) { 42 int y = e[i].to ; if(dep[y] || !e[i].flow) continue ; 43 dep[y] = dep[x] + 1 ; q.push(y) ; 44 } 45 } 46 return dep[t] ; 47 } 48 int F_dfs(int x,int minflow) { 49 if(x == t || minflow <= 0) return minflow ; 50 int fflow = 0 ; 51 for(int i=head[x];i;i=e[i].next) { 52 int y = e[i].to ; if(dep[y] != dep[x]+1) continue ; 53 int temp = F_dfs(y,min(minflow,e[i].flow)) ; 54 fflow += temp ; minflow -= temp ; 55 e[i].flow -= temp ; e[i^1].flow += temp ; 56 // out[x]++, out[y]-- ; printf("x:%d,y:%d\n",x,y) ; 57 if(!minflow) break ; 58 } 59 return fflow ; 60 } 61 62 void dfs(int x) { 63 vis[x] = 1 ; 64 printf("%d ",x) ; 65 for(int i=head[x];i;i=e[i].next) { 66 int y = e[i].to ; 67 if(ee[i].flow && !e[i].flow) { 68 dfs(y-n) ; break ; 69 } 70 } 71 } 72 73 int main() { 74 // freopen("path3.in","r",stdin) ; 75 // freopen("path3.out","w",stdout) ; 76 n = read(), m = read() ; 77 int x, y, ans = 0 ; s = (n<<1)+1, t = (n<<1)+2 ; 78 for(int i=1;i<=m;i++) { 79 x = read(), y = read() ; 80 add_edge(x,n+y,1) ; 81 } 82 for(int i=1;i<=n;i++) add_edge(s,i,1) ; 83 for(int i=n+1;i<=(n<<1);i++) add_edge(i,t,1) ; 84 while(F_bfs()) { 85 ans += F_dfs(s,INF) ; 86 } 87 // printf("ans:%d\n",ans) ; 88 ans = n-ans ; 89 for(int i=1;i<=n;i++) { 90 if(vis[i]) continue ; 91 bool flag = 0 ; 92 for(int j=head[i];j;j=e[j].next) { 93 if(ee[j].flow && !e[j].flow) { 94 flag = 1 ; break ; 95 } 96 } 97 if(flag) { 98 dfs(i) ; printf("\n") ; 99 } 100 } 101 printf("%d\n",ans) ; 102 return 0 ; 103 }
最小可交路径覆盖
把每个点与它可到达的点都按上题连上边,然后就按上题求即可。
魔术球问题
题目链接:https://www.luogu.org/problemnew/show/P2765
每增加一个球求一遍最小路径覆盖。连完新边之后直接在残量网络上求即可。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 #include<queue> 7 #include<bitset> 8 #define LL long long 9 #define RI register int 10 using namespace std; 11 const int INF = 0x7ffffff ; 12 const int N = 55 + 2 ; 13 const int M = 1600 + 10 ; 14 15 inline int read() { 16 int k = 0 , f = 1 ; char c = getchar() ; 17 for( ; !isdigit(c) ; c = getchar()) 18 if(c == ‘-‘) f = -1 ; 19 for( ; isdigit(c) ; c = getchar()) 20 k = k*10 + c-‘0‘ ; 21 return k*f ; 22 } 23 struct Edge { 24 int to, next, flow ; 25 }e[M*100], ee[M*100] ; 26 int n, s, t ; int head[M<<1], dep[M<<1] ; 27 bitset<M>vis ; 28 inline void add_edge(int x,int y,int ff) { 29 static int cnt = 1 ; 30 ee[++cnt].to = y, ee[cnt].next = head[x], ee[cnt].flow = ff ; 31 e[cnt].to = y, e[cnt].next = head[x], e[cnt].flow = ff, head[x] = cnt ; 32 ee[++cnt].to = x, ee[cnt].next = head[y], ee[cnt].flow = 0 ; 33 e[cnt].to = x, e[cnt].next = head[y], e[cnt].flow = 0, head[y] = cnt ; 34 } 35 36 inline bool F_bfs() { 37 memset(dep,0,sizeof(dep)) ; 38 queue<int>q ; q.push(s) ; dep[s] = 1 ; 39 while(!q.empty()) { 40 int x = q.front() ; q.pop() ; 41 for(int i=head[x];i;i=e[i].next) { 42 int y = e[i].to ; if(dep[y] || !e[i].flow) continue ; 43 dep[y] = dep[x] + 1 ; q.push(y) ; 44 } 45 } 46 return dep[t] ; 47 } 48 int F_dfs(int x,int minflow) { 49 // printf("xxx\n") ; 50 if(x == t || minflow <= 0) return minflow ; 51 int fflow = 0 ; 52 for(int i=head[x];i;i=e[i].next) { 53 int y = e[i].to ; if(dep[y] != dep[x]+1 || !e[i].flow) continue ; 54 int temp = F_dfs(y,min(minflow,e[i].flow)) ; 55 fflow += temp ; minflow -= temp ; 56 e[i].flow -= temp ; e[i^1].flow += temp ; 57 if(!minflow) break ; 58 } 59 return fflow ; 60 } 61 void dfs(int x) { 62 printf("%d ",x) ; vis[x] = 1 ; 63 for(int i=head[x];i;i=e[i].next) { 64 if(ee[i].flow && !e[i].flow) { 65 dfs(e[i].to-M) ; break ; 66 } 67 } 68 } 69 70 inline int sqr(int x) { return x*x ; } 71 int main() { 72 // freopen("balla.in","r",stdin) ; 73 // freopen("balla.out","w",stdout) ; 74 n = read() ; int nn = 0 ; s = (M<<1)-10, t = (M<<1)-9 ; 75 int ans = 0, maxx ; 76 while(1) { 77 nn++ ; 78 add_edge(s,nn,1) ; add_edge(nn+M,t,1) ; 79 for(int i=1;i<nn;i++) if(sqr(sqrt(i+nn)) == i+nn) add_edge(i,nn+M,1) ; 80 while(F_bfs()) ans += F_dfs(s,INF) ; 81 if(nn-ans > n) break ; 82 maxx = nn ; 83 } 84 printf("%d\n",maxx) ; 85 for(int x=1;x<=maxx;x++) { 86 if(vis[x]) continue ; 87 // for(int i=head[x];i;i=e[i].next) { 88 // if(ee[i].flow && !e[i].flow) { 89 dfs(x) ; printf("\n") ; // break ; 90 // } 91 // } 92 } 93 return 0 ; 94 }