[费用流]数字配对,新生舞会
文章目錄
- T1:數(shù)字配對
- 題目
- 題解
- CODE
- T2:新生舞會
- 題目
- 題解
- CODE(最大費用最大流版)
- CODE(最小費用最大流版)
T1:數(shù)字配對
題目
有 n 種數(shù)字,第 i 種數(shù)字是 ai、有 bi 個,權(quán)值是 ci。
若兩個數(shù)字 ai、aj 滿足,ai 是 aj 的倍數(shù),且 ai/aj 是一個質(zhì)數(shù),
那么這兩個數(shù)字可以配對,并獲得 ci×cj 的價值。
一個數(shù)字只能參與一次配對,可以不參與配對。
在獲得的價值總和不小于 0 的前提下,求最多進行多少次配對
Input
第一行一個整數(shù) n。
第二行 n 個整數(shù) a1、a2、……、an。
第三行 n 個整數(shù) b1、b2、……、bn。
第四行 n 個整數(shù) c1、c2、……、cn。
Output
一行一個數(shù),最多進行多少次配對
Sample Input
3
2 4 8
2 200 7
-1 -2 1
Sample Output
4
Hint
n≤200,ai≤109,bi≤105,∣ci∣≤105n≤200,ai≤10^9,bi≤10^5,∣ci∣≤10^5n≤200,ai≤109,bi≤105,∣ci∣≤105
題解
首先我們考慮如何判斷ai/ajai/ajai/aj為質(zhì)數(shù),肯定的我們把aiaiai和ajajaj進行標準的唯一質(zhì)因數(shù)分解
ajajaj的所有因數(shù)aiaiai都有,并且ajajaj的每一個因數(shù)的冪≤≤≤該因數(shù)在aiaiai中的冪,這樣才能保證ai%aj==0ai\%aj==0ai%aj==0
那么接下來就判斷ai/ajai/ajai/aj等于一個質(zhì)數(shù),其實就是對于一個質(zhì)因數(shù)xxx,設(shè)xxx在ajajaj中的冪為m1m1m1,在aiaiai中的冪為m2m2m2,滿足m1==m2∣∣m1==m2?1m1==m2||m1==m2-1m1==m2∣∣m1==m2?1且只會有一個質(zhì)因數(shù)在ajajaj的冪小于aiaiai中的冪其他的都是等于,不然兩個質(zhì)因數(shù)乘起來還是一個合數(shù)
但是我們在建圖時會遇到一個問題,如果每一個點都與超級源點和超級匯點建邊,有可能直接就只流了該點與源點的邊馬上就流回了匯點,并沒有與其他點產(chǎn)生關(guān)系
那么我們?yōu)榱吮苊膺@個問題,就抓住ai/ajai/ajai/aj為一個質(zhì)數(shù)的特殊性,我們發(fā)現(xiàn)aiaiai的所有質(zhì)因數(shù)的冪的和一定等于ajajaj的所有質(zhì)因數(shù)的冪的和+1+1+1
所以如果兩個數(shù)的質(zhì)因數(shù)的冪的和都是奇數(shù)或者都是偶數(shù),那么兩數(shù)之間一定是不會滿足關(guān)系的,和至少都差了2,那么我們就把奇數(shù)劃分成一個區(qū)域,與源點建邊,流量就是個數(shù),偶數(shù)劃分成一個區(qū)域與匯點建邊,流量也是個數(shù),奇數(shù)和偶數(shù)之間判斷冪的和是否只差1,滿足的就建邊,費用就是能獲得的貢獻,流量就流無限
CODE
#include <queue> #include <cmath> #include <cstdio> #include <cstring> using namespace std; #define INF 1e9 #define int long long #define MAXN 500 struct node {int v, w, next, c, flow; }edge[MAXN * MAXN]; queue < int > q; int cnt, n, m, s, t; int head[MAXN], dis[MAXN], pre[MAXN], a[MAXN], v[MAXN], tot[MAXN]; bool vis[MAXN];void add ( int x, int y, int flow, int cost ) {edge[cnt].next = head[x];edge[cnt].v = y;edge[cnt].w = cost;edge[cnt].c = flow;edge[cnt].flow = 0;head[x] = cnt ++; }bool spfa () {memset ( vis, 0, sizeof ( vis ) ); memset ( dis, -0x7f, sizeof ( dis ) );memset ( pre, -1, sizeof ( pre ) );while ( ! q.empty() )q.pop();q.push( s );dis[s] = 0;vis[s] = 1;while ( ! q.empty() ) {int u = q.front();q.pop();vis[u] = 0;for ( int i = head[u];~ i;i = edge[i].next ) {int v = edge[i].v;if ( dis[v] < dis[u] + edge[i].w && edge[i].c > edge[i].flow ) {dis[v] = dis[u] + edge[i].w;pre[v] = i;if ( ! vis[v] ) {q.push( v );vis[v] = 1;}}}} return pre[t] != -1; }int Fabs ( int x ) {if ( x < 0 )return -x;return x; }void MCMF ( int &maxflow, int &mincost ) {maxflow = mincost = 0;while ( spfa() ) {int MIN = INF;for ( int i = pre[t];~ i;i = pre[edge[i ^ 1].v] )MIN = min ( MIN, edge[i].c - edge[i].flow );if ( mincost + MIN * dis[t] < 0 ) {//不能流完就盡量流,因為這個delta是單峰,一旦下降便不會在上升 maxflow += ( mincost / -dis[t] );break;}maxflow += MIN;for ( int i = pre[t];~ i;i = pre[edge[i ^ 1].v] ) {edge[i].flow += MIN;edge[i ^ 1].flow -= MIN;mincost += MIN * edge[i].w;}} }signed main() {memset ( head, -1, sizeof ( head ) );scanf ( "%lld", &n );for ( int i = 1;i <= n;i ++ ) {scanf ( "%lld", &a[i] );int x = a[i];for ( int j = 2;j * j <= x;j ++)while ( x % j == 0 ) {x /= j;tot[i] ++;}if ( x != 1 )tot[i] ++;}s = 0;t = n + 1;for ( int i = 1;i <= n;i ++ ) {int w;scanf ( "%lld", &w );if ( tot[i] & 1 ) {add ( s, i, w, 0 );add ( i, s, 0, 0 );}else {add ( i, t, w, 0 );add ( t, i, 0, 0 );}}for ( int i = 1;i <= n;i ++ )scanf ( "%lld", &v[i] );for ( int i = 1;i <= n;i ++ ) {if ( tot[i] & 1 ) {for ( int j = 1;j <= n;j ++ )if ( Fabs ( tot[i] - tot[j] ) == 1 && ( a[i] % a[j] == 0 || a[j] % a[i] == 0 ) ) {add ( i, j, INF, v[i] * v[j] );add ( j, i, 0, - ( v[i] * v[j] ) );}}}int maxflow, mincost;MCMF ( maxflow, mincost );printf ( "%lld", maxflow );return 0; }T2:新生舞會
題目
學校組織了一次新生舞會,Cathy作為經(jīng)驗豐富的老學姐,負責為同學們安排舞伴。有n個男生和n個女生參加舞會
買一個男生和一個女生一起跳舞,互為舞伴。Cathy收集了這些同學之間的關(guān)系,比如兩個人之前認識沒計算得出 a[i][j] ,表示第i個男生和第j個女生一起跳舞時他們的喜悅程度。Cathy還需要考慮兩個人一起跳舞是否方便,比如身高體重差別會不會太大,計算得出 b[i][j],表示第i個男生和第j個女生一起跳舞時的不協(xié)調(diào)程度。
當然,還需要考慮很多其他問題。Cathy想先用一個程序通過a[i][j]和b[i][j]求出一種方案,再手動對方案進行微調(diào)。Cathy找到你,希望你幫她寫那個程序。一個方案中有n對舞伴,假設(shè)沒對舞伴的喜悅程度分別是a’1,a’2,…,a’n,
假設(shè)每對舞伴的不協(xié)調(diào)程度分別是b’1,b’2,…,b’n。令
C=(a’1+a’2+…+a’n)/(b’1+b’2+…+b’n),Cathy希望C值最大
Input
第一行一個整數(shù)n。
接下來n行,每行n個整數(shù),第i行第j個數(shù)表示a[i][j]。
接下來n行,每行n個整數(shù),第i行第j個數(shù)表示b[i][j]。
1<=n<=100,1<=a[i][j],b[i][j]<=10^4
Output
一行一個數(shù),表示C的最大值。四舍五入保留6位小數(shù),選手輸出的小數(shù)需要與標準輸出相等
Sample Input
3
19 17 16
25 24 23
35 36 31
9 5 6
3 4 2
7 8 9
Sample Output
5.357143
題解
轉(zhuǎn)化一下答案,即
C=∑i=1nai∑i=1nbiC=\frac{\sum_{i=1}^n a_i}{\sum_{i=1}^{n}b_i}C=∑i=1n?bi?∑i=1n?ai??
–>
C∑i=1nbi=∑i=1naiC\sum_{i=1}^nb_i=\sum_{i=1}^na_iCi=1∑n?bi?=i=1∑n?ai?
–>
∑i=1nai?C∑i=1nbi=0\sum_{i=1}^na_i-C\sum_{i=1}^nb_i=0i=1∑n?ai??Ci=1∑n?bi?=0
所以我們可以考慮二分答案CCC,判斷∑i=1nai?C∑i=1nbi≥0\sum_{i=1}^na_i-C\sum_{i=1}^nb_i≥0∑i=1n?ai??C∑i=1n?bi?≥0,
男生與超級源點建邊,女生與超級匯點建邊,男女生之間的建邊就是aij?xbija_{ij}-xb_{ij}aij??xbij?的費用
跑一個最大費用最大流,
分享另一個想法:我們可以將以上的思路全部去一個反,就變成判斷∑i=1nai?C∑i=1nbi<0\sum_{i=1}^na_i-C\sum_{i=1}^nb_i<0∑i=1n?ai??C∑i=1n?bi?<0那就是跑最小費用最大流
仙女都給了代碼,
CODE(最大費用最大流版)
#include <queue> #include <vector> #include <cstdio> #include <cstring> using namespace std; #define eps 1e-7 #define INF 2e9 #define MAXM 200005 #define MAXN 105 queue < int > q; int cnt, n, s, t; int head[MAXN * 10], pre[MAXN * 10]; double dis[MAXM], w[MAXM], c[MAXM], flow[MAXM]; int a[MAXN][MAXN], b[MAXN][MAXN], v[MAXM], nxt[MAXM]; bool vis[MAXM];void add ( int x, int y, double flow_, double cost ) {nxt[cnt] = head[x];v[cnt] = y;w[cnt] = cost;c[cnt] = flow_;flow[cnt] = 0;head[x] = cnt ++; }bool spfa () {for ( int i = s;i <= t;i ++ ) {dis[i] = -INF;pre[i] = -1;}q.push( s );dis[s] = 0;vis[s] = 1;while ( ! q.empty() ) {int u = q.front();q.pop();vis[u] = 0;for ( int i = head[u];~ i;i = nxt[i] ) {if ( dis[v[i]] < dis[u] + w[i] && c[i] > flow[i] ) {dis[v[i]] = dis[u] + w[i];pre[v[i]] = i;if ( ! vis[v[i]] ) {q.push( v[i] );vis[v[i]] = 1;}}}}return pre[t] != -1; }bool MCMF () {double mincost = 0;while ( spfa() ) {for ( int i = pre[t];~ i;i = pre[v[i ^ 1]] ) {flow[i] ++;flow[i ^ 1] --;}mincost += dis[t];}return mincost >= 0; }int main() {scanf ( "%d", &n );s = 0;t = n << 1 | 1;double sum = 0;for ( int i = 1;i <= n;i ++ )for ( int j = 1;j <= n;j ++ ) {scanf ( "%d", &a[i][j] ); sum += a[i][j];}for ( int i = 1;i <= n;i ++ )for ( int j = 1;j <= n;j ++ )scanf ( "%d", &b[i][j] );double l = 0, r = sum;while ( r - l > eps ) {double mid = ( l + r ) / 2;memset ( head, -1, sizeof ( head ) );cnt = 0;for ( int i = 1;i <= n;i ++ ) {add ( s, i, 1, 0 );add ( i, s, 0, 0 );add ( i + n, t, 1, 0 );add ( t, i + n, 0, 0 );}for ( int i = 1;i <= n;i ++ )for ( int j = 1;j <= n;j ++ ) {add ( i, j + n, 1, a[i][j] * 1.0 - 1.0 * b[i][j] * mid );add ( j + n, i, 0, - ( a[i][j] * 1.0 - 1.0 * b[i][j] * mid ) );}if ( MCMF() )l = mid;elser = mid;}printf ( "%.6f", l );return 0; }CODE(最小費用最大流版)
#include <queue> #include <vector> #include <cstdio> #include <cstring> using namespace std; #define eps 1e-7 #define INF 2e9 #define MAXM 200005 #define MAXN 105 queue < int > q; int cnt, n, s, t; int head[MAXN * 10], pre[MAXN * 10]; double dis[MAXM], w[MAXM], c[MAXM], flow[MAXM]; int a[MAXN][MAXN], b[MAXN][MAXN], v[MAXM], nxt[MAXM]; bool vis[MAXM];void add ( int x, int y, double flow_, double cost ) {nxt[cnt] = head[x];v[cnt] = y;w[cnt] = cost;c[cnt] = flow_;flow[cnt] = 0;head[x] = cnt ++; }bool spfa () {for ( int i = s;i <= t;i ++ ) {dis[i] = INF;pre[i] = -1;}q.push( s );dis[s] = 0;vis[s] = 1;while ( ! q.empty() ) {int u = q.front();q.pop();vis[u] = 0;for ( int i = head[u];~ i;i = nxt[i] ) {if ( dis[v[i]] > dis[u] + w[i] && c[i] > flow[i] ) {dis[v[i]] = dis[u] + w[i];pre[v[i]] = i;if ( ! vis[v[i]] ) {q.push( v[i] );vis[v[i]] = 1;}}}}return pre[t] != -1; }bool MCMF () {double mincost = 0;while ( spfa() ) {for ( int i = pre[t];~ i;i = pre[v[i ^ 1]] ) {flow[i] ++;flow[i ^ 1] --;}mincost += dis[t];}return mincost < 0; }int main() {scanf ( "%d", &n );s = 0;t = n << 1 | 1;double sum = 0;for ( int i = 1;i <= n;i ++ )for ( int j = 1;j <= n;j ++ ) {scanf ( "%d", &a[i][j] ); sum += a[i][j];}for ( int i = 1;i <= n;i ++ )for ( int j = 1;j <= n;j ++ )scanf ( "%d", &b[i][j] );double l = 0, r = sum;while ( r - l > eps ) {double mid = ( l + r ) / 2;memset ( head, -1, sizeof ( head ) );cnt = 0;for ( int i = 1;i <= n;i ++ ) {add ( s, i, 1, 0 );add ( i, s, 0, 0 );add ( i + n, t, 1, 0 );add ( t, i + n, 0, 0 );}for ( int i = 1;i <= n;i ++ )for ( int j = 1;j <= n;j ++ ) {add ( i, j + n, 1, - ( a[i][j] * 1.0 - 1.0 * b[i][j] * mid ) );add ( j + n, i, 0, a[i][j] * 1.0 - 1.0 * b[i][j] * mid );}if ( MCMF() )l = mid;elser = mid;}printf ( "%.6f", l );return 0; }
byebye,點個贊
總結(jié)
以上是生活随笔為你收集整理的[费用流]数字配对,新生舞会的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 路由器怎么安装和设置路由器怎么安装设置使
- 下一篇: 消息称苹果新款 iPad Pro 的 O