博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[SDOI2016 Round1] 数字配对
阅读量:5106 次
发布时间:2019-06-13

本文共 5705 字,大约阅读时间需要 19 分钟。

COGS 2221. [SDOI2016 Round1] 数字配对

★★★   输入文件:menci_pair.in   输出文件:menci_pair.out   简单对比

时间限制:1 s   内存限制:128 MB

【题目描述】

有 n 种数字,第 i 种数字是 ai、有 bi 个,权值是 ci

若两个数字 aiaj 满足,ai 是 aj 的倍数,且 aiaj 是一个质数,那么这两个数字可以配对,并获得 ci×cj 的价值。

一个数字只能参与一次配对,可以不参与配对。

在获得的价值总和不小于 0 的前提下,求最多进行多少次配对。

【输入格式】

第一行一个整数 n

第二行 n 个整数 a1a2、……、an
第三行 n 个整数 b1b2、……、bn
第四行 n 个整数 c1c2、……、cn

【输出格式】

一行一个数,最多进行多少次配对。

【样例输入】

3

2 4 8

2 200 7

-1 -2 1

【样例输出】

4

【提示】

测试点 1 ~ 3:n10ai109bi=1ci∣≤105

测试点 4 ~ 5:n200ai109bi105ci=0
测试点 6 ~ 10:n200ai109bi105ci∣≤105

【来源】

SDOI2016 Round1 Day1

费用流u

构图方法:(以样例为例)

1、2可以配对;2、3可以配对

注意,这里若a与b可以配对,则既要由a向b连边,又要由b向a连同样的边,最后答案除以2

原因:

1、如果只由a向b连,那么如果又有一条边由c连向a,边流量都为inf,这样从源点向a用了,由a向汇点又用了,应该统计的是2次之和,但实际只统计了其中一次

2、a向b连边m、b向a连同样的边n,这样费用流跑m一定跑n,这样就可以把1中2次汇总,因为对应边流量相等,所以答案要除2

因为要总价值和>=0,所以每次跑最大费用,

如果本次跑出的最大价值+已累积的价值>=0,继续跑

反之,次数+已累计价值/-本次单位流量最大费用,结束

因为累计价值不可能为负,而题目要求总价值和>=0,若满足反之条件,本次最大费用<0 且 本次最大费用总和绝对值>已累计价值,所以就看已累计价值最大能抵消多少次本次的负价值

#include
#include
#include
#include
#include
using namespace std;int a[201],b[201],c[201];int tot=1,src,dec;int front1[410],from[160001],next1[160001],to1[160001];bool v[410];int n,fa[410]; long long dis[410],sum_cost,cost[160001],cap[160001];int sum_flow;queue
que;bool judge(int x){ for(int i=2;i<=sqrt(x);i++) if(x%i==0) return false; return true;}void insert_edge(int u,int v,long long w,long long val){ to1[++tot]=v;from[tot]=u;next1[tot]=front1[u];front1[u]=tot;cap[tot]=w;cost[tot]=val; to1[++tot]=u;from[tot]=v;next1[tot]=front1[v];front1[v]=tot;cap[tot]=0;cost[tot]=-val;}bool spfa(){ for(int i=1;i<=dec;i++) dis[i]=-1e15,fa[i]=0; memset(v,0,sizeof(v)); que.push(src);v[src]=true; dis[0]=0; while(!que.empty()) { int now=que.front(); que.pop();v[now]=false; for(int i=front1[now];i;i=next1[i]) { if(dis[now]+cost[i]>dis[to1[i]]&&cap[i]>0) { dis[to1[i]]=dis[now]+cost[i]; fa[to1[i]]=i; if(!v[to1[i]]) { que.push(to1[i]); v[to1[i]]=true; } } } } if(dis[dec]!=-1e10) return true; return false;}void work(){ while(spfa()) { long long tmp=1e15,k=0; for(int i=fa[dec];i;i=fa[from[i]]) tmp=min(cap[i],tmp); if(sum_cost+dis[dec]*1ll*tmp>=0) { sum_cost+=dis[dec]*tmp;sum_flow+=tmp; for(int i=fa[dec];i;i=fa[from[i]]) { cap[i]-=tmp;cap[i^1]+=tmp; } } else { sum_flow+=int(sum_cost/abs(dis[dec])); break; } } printf("%d",sum_flow/2); return ;}int main(){ freopen("menci_pair.in","r",stdin); freopen("menci_pair.out","w",stdout); scanf("%d",&n); dec=n+1<<1; for(int i=1;i<=n;i++) scanf("%d",&a[i]); for(int i=1;i<=n;i++) scanf("%d",&b[i]); for(int i=1;i<=n;i++) scanf("%d",&c[i]); for(int i=1;i<=n;i++) insert_edge(src,i<<1,b[i],0); for(int i=1;i<=n;i++) insert_edge(i<<1|1,dec,b[i],0); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) { if(a[i]<=a[j]) continue; if(a[i]%a[j]==0&&judge(a[i]/a[j])) { insert_edge(j<<1,i<<1|1,1e15,1ll*c[i]*c[j]); insert_edge(i<<1,j<<1|1,1e15,1ll*c[i]*c[j]); } } work();}

学长说了另外2种方法:

1、根据分解质因数的指数和的奇偶性,将所有点分为2个集合,构建二分图(标解,不想写就没写)

2、根据整除关系构成的链,将所有点分为2个集合,每条链的起点在哪个集合里随便,构建二分图,与1不同的地方就是链的起点在哪个集合的问题

(这个写了,然而调了一晚上+半上午,COGS提交最终3A 1W 1RE 5T ,法2正确性、代码正确性有待验证)

#include
#include
#include
#include
#include
using namespace std;int a[201],b[201],c[201];int front[210],next[160001],to[160001],tot,src,dec;int front1[210],from[160001],next1[160001],to1[160001];bool use_in[210],use_out[210],v[210];int n,fa[210]; long long dis[210],sum_cost,cost[160001],cap[160001];int sum_flow;struct node1{ int point,id;};queue
q;queue
que;bool judge(int x){ for(int i=2;i<=sqrt(x);i++) if(x%i==0) return false; return true;}void add(int u,int v){ to[++tot]=v;next[tot]=front[u];front[u]=tot; use_in[v]=true;use_out[u]=true;}void insert_edge(int u,int v,long long w,long long val){ to1[++tot]=v;from[tot]=u;next1[tot]=front1[u];front1[u]=tot;cap[tot]=w;cost[tot]=val; to1[++tot]=u;from[tot]=v;next1[tot]=front1[v];front1[v]=tot;cap[tot]=0;cost[tot]=-val;}bool spfa(){ for(int i=1;i<=dec;i++) dis[i]=-1e15,fa[i]=0; que.push(src);v[src]=true; while(!que.empty()) { int now=que.front(); que.pop();v[now]=false; for(int i=front1[now];i;i=next1[i]) { if(dis[now]+1ll*cost[i]>dis[to1[i]]&&cap[i]>0) { dis[to1[i]]=dis[now]+1ll*cost[i]; fa[to1[i]]=i; if(!v[to1[i]]) { que.push(to1[i]); v[to1[i]]=true; } } } } if(dis[dec]!=-1e15) return true; return false;}void work(){ while(spfa()) { long long tmp=1e15; for(int i=fa[dec];i;i=fa[from[i]]) tmp=min(cap[i],tmp); if(sum_cost+dis[dec]*1ll*tmp>=0) { sum_cost+=dis[dec]*tmp;sum_flow+=tmp; for(int i=fa[dec];i;i=fa[from[i]]) { cap[i]-=tmp;cap[i^1]+=tmp; } } else { sum_flow+=int(sum_cost/abs(dis[dec])); break; } } printf("%d",sum_flow); return ;}int main(){ freopen("menci_pair.in","r",stdin); freopen("menci_pair.out","w",stdout); scanf("%d",&n); dec=n+1; for(int i=1;i<=n;i++) scanf("%d",&a[i]); for(int i=1;i<=n;i++) scanf("%d",&b[i]); for(int i=1;i<=n;i++) scanf("%d",&c[i]); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) { if(a[i]<=a[j]) continue; if(!a[j]) continue; if(a[i]%a[j]==0&&judge(a[i]/a[j])) { add(j,i); } } tot=1; for(int i=1;i<=n;i++) if(!use_in[i]) { insert_edge(src,i,b[i],0); q.push((node1){i,1}); } while(!q.empty()) { node1 now=q.front();q.pop(); for(int i=front[now.point];i;i=next[i]) { int t=to[i]; if(now.id%2) { insert_edge(now.point,t,1e15,1ll*c[now.point]*c[t]); insert_edge(t,dec,b[t],0); q.push((node1){t,now.id+1}); } else { insert_edge(t,now.point,1e15,1ll*c[now.point]*c[t]); insert_edge(src,t,b[t],0); q.push((node1){t,now.id+1}); } } } work();}
错误代码

 

转载于:https://www.cnblogs.com/TheRoadToTheGold/p/6508988.html

你可能感兴趣的文章
js 获得日期相差天数
查看>>
速度环加位置环进行电机控制
查看>>
发布.net core项目 System.AggregateException: 发生一个或多个错误
查看>>
空间滤波
查看>>
学习Memcached:1基本配置与安装
查看>>
C#.NET 生成分页SQL代码(NOT IN/TOP TOP/Top MAX)三种方法,支持ACCESS
查看>>
让爱编译通过
查看>>
java通过url调用远程接口返回json数据
查看>>
【循序渐进学Python】5.Python常用流程控制及其他语句
查看>>
深入理解linux启动过程
查看>>
Python建立Web应用
查看>>
php
查看>>
使用requests模块post payload请求
查看>>
javascript console
查看>>
Linux时间子系统之(六):POSIX timer
查看>>
Linux内存管理 (20)最新更新和展望
查看>>
js实现图片自适应
查看>>
不要重复你自己(复用代码)
查看>>
再读三命通会(7)
查看>>
【操作系统】【进程】进程各状态关系图
查看>>