[费用流]数字配对,新生舞会
文章目录
- T1:数字配对
- 题目
- 题解
- CODE
- T2:新生舞会
- 题目
- 题解
- CODE(最大费用最大流版)
- CODE(最小费用最大流版)
T1:数字配对
题目
有 n 种数字,第 i 种数字是 ai、有 bi 个,权值是 ci。
若两个数字 ai、aj 满足,ai 是 aj 的倍数,且 ai/aj 是一个质数,
那么这两个数字可以配对,并获得 ci×cj 的价值。
一个数字只能参与一次配对,可以不参与配对。
在获得的价值总和不小于 0 的前提下,求最多进行多少次配对
Input
第一行一个整数 n。
第二行 n 个整数 a1、a2、……、an。
第三行 n 个整数 b1、b2、……、bn。
第四行 n 个整数 c1、c2、……、cn。
Output
一行一个数,最多进行多少次配对
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为质数,肯定的我们把aiaiai和ajajaj进行标准的唯一质因数分解
ajajaj的所有因数aiaiai都有,并且ajajaj的每一个因数的幂≤≤≤该因数在aiaiai中的幂,这样才能保证ai%aj==0ai\%aj==0ai%aj==0
那么接下来就判断ai/ajai/ajai/aj等于一个质数,其实就是对于一个质因数xxx,设xxx在ajajaj中的幂为m1m1m1,在aiaiai中的幂为m2m2m2,满足m1==m2∣∣m1==m2−1m1==m2||m1==m2-1m1==m2∣∣m1==m2−1且只会有一个质因数在ajajaj的幂小于aiaiai中的幂其他的都是等于,不然两个质因数乘起来还是一个合数
但是我们在建图时会遇到一个问题,如果每一个点都与超级源点和超级汇点建边,有可能直接就只流了该点与源点的边马上就流回了汇点,并没有与其他点产生关系
那么我们为了避免这个问题,就抓住ai/ajai/ajai/aj为一个质数的特殊性,我们发现aiaiai的所有质因数的幂的和一定等于ajajaj的所有质因数的幂的和+1+1+1
所以如果两个数的质因数的幂的和都是奇数或者都是偶数,那么两数之间一定是不会满足关系的,和至少都差了2,那么我们就把奇数划分成一个区域,与源点建边,流量就是个数,偶数划分成一个区域与汇点建边,流量也是个数,奇数和偶数之间判断幂的和是否只差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作为经验丰富的老学姐,负责为同学们安排舞伴。有n个男生和n个女生参加舞会
买一个男生和一个女生一起跳舞,互为舞伴。Cathy收集了这些同学之间的关系,比如两个人之前认识没计算得出 a[i][j] ,表示第i个男生和第j个女生一起跳舞时他们的喜悦程度。Cathy还需要考虑两个人一起跳舞是否方便,比如身高体重差别会不会太大,计算得出 b[i][j],表示第i个男生和第j个女生一起跳舞时的不协调程度。
当然,还需要考虑很多其他问题。Cathy想先用一个程序通过a[i][j]和b[i][j]求出一种方案,再手动对方案进行微调。Cathy找到你,希望你帮她写那个程序。一个方案中有n对舞伴,假设没对舞伴的喜悦程度分别是a’1,a’2,…,a’n,
假设每对舞伴的不协调程度分别是b’1,b’2,…,b’n。令
C=(a’1+a’2+…+a’n)/(b’1+b’2+…+b’n),Cathy希望C值最大
Input
第一行一个整数n。
接下来n行,每行n个整数,第i行第j个数表示a[i][j]。
接下来n行,每行n个整数,第i行第j个数表示b[i][j]。
1<=n<=100,1<=a[i][j],b[i][j]<=10^4
Output
一行一个数,表示C的最大值。四舍五入保留6位小数,选手输出的小数需要与标准输出相等
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
题解
转化一下答案,即
C=∑i=1nai∑i=1nbiC=\frac{\sum_{i=1}^n a_i}{\sum_{i=1}^{n}b_i}C=∑i=1nbi∑i=1nai
–>
C∑i=1nbi=∑i=1naiC\sum_{i=1}^nb_i=\sum_{i=1}^na_iCi=1∑nbi=i=1∑nai
–>
∑i=1nai−C∑i=1nbi=0\sum_{i=1}^na_i-C\sum_{i=1}^nb_i=0i=1∑nai−Ci=1∑nbi=0
所以我们可以考虑二分答案CCC,判断∑i=1nai−C∑i=1nbi≥0\sum_{i=1}^na_i-C\sum_{i=1}^nb_i≥0∑i=1nai−C∑i=1nbi≥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=1nai−C∑i=1nbi<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,点个赞
[费用流]数字配对,新生舞会相关推荐
- P4068-[SDOI2016]数字配对【二分,费用流】
正题 题目链接:https://www.luogu.com.cn/problem/P4068 题目大意 nnn种数字,第iii个是aia_iai,有bib_ibi个,价值为ci∗cjc_i*c_j ...
- bzoj 4514: [Sdoi2016]数字配对(二分图+费用最大流)
4514: [Sdoi2016]数字配对 Time Limit: 10 Sec Memory Limit: 128 MB Submit: 1840 Solved: 703 [Submit][Sta ...
- P3705-[SDOI2017]新生舞会【0/1分数规划,费用流】
正题 题目链接:https://www.luogu.com.cn/problem/P3705 题目大意 nnn对人,给出两个n∗nn*nn∗n的矩形a,ba,ba,b.求一个nnn配对满足∑i=1na ...
- bzoj4514: [Sdoi2016]数字配对(费用流)
传送门 ps:费用流增广的时候费用和流量打反了--调了一个多小时 每个数只能参与一次配对,那么这就是一个匹配嘛 我们先把每个数分解质因数,记质因子总个数为$cnt_i$,那如果$a_i/a_j$是质数 ...
- 【BZOJ4514】数字配对,费用流
传送门 题面: 写在前面:网络流练习太少-- 思路:费用流,最大或最小随意,看你给费用的符号,建图的话是把数分成两部分,分别是奇数个质因子和偶数个质因子,然后通过题目给出的关系连边(分部分的原因是形成 ...
- 【网络流24题】【LOJ6010】数字梯形(费用流)
problem 给定一个n行的数字梯形,第一行有m个数字 从第一行的每个数字开始往左下或右下移动到底,累加路径上的值 求数字总和最大. 满足限制: 1.路径互不相交 2.路径仅在数字结点处相交 3.路 ...
- SCUT - 48 - 飞行员的配对方案 - 费用流
https://scut.online/p/48 一道二分图匹配,跑费用流就可以过了(其实最大流都可以了). #include<bits/stdc++.h> #define MAXN_ 5 ...
- [SDOI2017]新生舞会
题目描述 学校组织了一次新生舞会,Cathy作为经验丰富的老学姐,负责为同学们安排舞伴. 有个男生和个女生参加舞会买一个男生和一个女生一起跳舞,互为舞伴. Cathy收集了这些同学之间的关系,比如两个 ...
- bzoj4514[Sdoi2016]数字配对
bzoj4514[Sdoi2016]数字配对 题意: 有 n 种数字,第 i 种数字是 ai.有 bi 个,权值是 ci.若两个数字 ai.aj 满足ai 是 aj 的倍数且 ai/aj 是一个质数, ...
最新文章
- CodeForces 631D Messenger
- MySQL的恢复脚本
- IHttpHandler的妙用(2):防盗链!我的资源只有我的用户才能下载
- 握手失败_拜托了,看完这篇别再问我什么是TCP三次握手和四次挥手
- 艾伟:ASP.NET MVC,深入浅出IModelBinder,在Post方式下慎用HtmlHelper
- python扫描ip的端口打开情况
- [LOJ #521]「LibreOJ β Round #3」绯色 IOI(抵达)(结论)
- [SQL Server]用于压力测试和性能分析的两个支持实用工具[转]
- zookeeper在linux环境安装
- 【leetcode】Trips and Users
- [UESTC SC T4] Chika 的烦恼
- 达内JAVA云笔记_达内云笔记项目完整代码+注释
- C# winform 如何让TextBox文本内容垂直居中?
- Reeder 5 for Mac(RSS阅读器)
- 【数据结构实验一】线性表
- 微信小程序连接emqx服务器实现数据交互
- K8S(二)安装配置篇
- 干货|最全亚马逊账号关联知识都在这 10条
- VB中使用表查询法获取CRC16
- 删除了大文件,但是磁盘并没有释放
热门文章
- bytecode java_Java 字节码解读
- 初二物理模型有哪些_初二是成绩下滑的高危期,做好这5点成绩涨涨涨!(附全学科提升技巧,家长转给孩子!)...
- element 方法返回的boolean被当成字符串了_13个需要知道的方法:使用 JavaScript 来操作 DOM...
- windows制作定时关机脚本_自动关机、自动打开程序… 让Windows自动执行任何操作...
- 机器学习之数据预处理——归一化,标准化
- 吴恩达DeepLearningCourse3-结构化机器学习项目
- android socket 服务端,Android socket 服务端
- php yaf smarty,Yaf 结合用户自定义的视图(模板)引擎Smarty(Yaf + Smarty)
- java while do循环_c语言中,while 和 do while 循环的主要区别是( )
- C++实现拓扑排序(vector模拟邻接表存储,栈实现)