1. 程式人生 > >洛谷P2765 魔術球問題 網路流之最小路徑覆蓋

洛谷P2765 魔術球問題 網路流之最小路徑覆蓋

P2756P2756

思路:k根柱子相當於k條路徑覆蓋,求最多能放n個球的n的大小。 根據每個球相鄰球必須相加為平方數可以建邊,然後由: 最小路徑覆蓋 = n - 最大二分匹配, 可以列舉n,也可以二分查詢(因為球數n隨柱子k的增大是單調不遞減的)。 這樣可以得到球數n的大小。 然後輸出每條簡單路,和求最小路徑覆蓋一樣,每次增廣成功記錄連線的點就行了。 Code:

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
const int AX = 1e4 + 666 ;
int n ; 
int cur[
AX] ; int head[AX] ; int d[AX] ; int nxt[AX]; int s = 0 , t = 6666 ; struct Node{ int u , v , flow , next1 ; Node( int u = 0 , int v = 0 , int flow = 0 , int next1 = 0 ):u(u),v(v),flow(flow),next1(next1){} }G[AX*10]; int tot ; void addEdge( int u , int v , int w ){ G[tot] = Node( u , v , w , head[u] )
; head[u] = tot++ ; G[tot] = Node( v , u , 0 , head[v] ) ; head[v] = tot++ ; } bool bfs(){ memset( d , -1 , sizeof(d) ) ; d[s] = 0 ; queue<int>q; q.push(s); while( !q.empty() ) { int u = q.front() ; q.pop() ; for( int i = head[u] ; ~i ; i = G[i].next1 ) { int v = G[i].v ; if
( d[v] == -1 && G[i].flow ){ d[v] = d[u] + 1 ; q.push(v) ; if( v == t ) return true; } } } return ~d[t] ; } int dfs( int u , int cap ){ if( u == t || !cap ) return cap ; int r = 0 ; for( int i = cur[u] ; ~i ; i = G[i].next1 ){ int v = G[i].v ; if( G[i].flow && d[v] == d[u] + 1 ){ int tmp = min( G[i].flow , cap - r ) ; cur[u] = i ; tmp = dfs( v , tmp ); r += tmp ; if( tmp ){ nxt[u] = v ; } G[i].flow -= tmp ; G[i^1].flow += tmp ; if( r == cap ) break ; } } if( !r ) d[u] -= 2 ; return r ; } int Dinic(){ int cnt = 0 ; int tmp ; while( bfs() ){ memcpy( cur , head , sizeof(head) ) ; while( tmp = dfs( s , INF ) ) cnt += tmp ; } return cnt ; } int k ; bool solve( int n ) { tot = 0 ; memset( nxt , -1 , sizeof(nxt) ); memset( head , -1 , sizeof(head) ) ; for( int i = 1 ; i <= n ; i++ ){ for( int j = i + 1 ; j <= n ; j++ ){ int x = sqrt(i + j) ; if( x * x == ( i + j ) ) { addEdge( i , n + j , 1 ) ; } } } for( int i = 1 ; i <= n ; i++ ){ addEdge( s , i , 1 ) ; addEdge( i + n , t , 1 ) ; } int flow = Dinic() ; n -= flow ; return ( k < n ) ; } int main(){ scanf("%d",&k) ; int l = 0 , r = 1600 ; while( l < r ){ int mid = ( l + r ) / 2 ; if( solve(mid) ){ r = mid ; }else l = mid + 1 ; } n = l - 1 ; solve( n ) ; printf("%d\n",n); for( int i = 1 ; i <= n ; i++ ){ if( nxt[i] == -1 ) continue ; int u = i ; while( ~u ){ if( u >= n ) u -= n ; printf("%d ",u); int x = nxt[u]; nxt[u] = -1; u = x ; } printf("\n"); } return 0 ; }