bzoj 4514: [Sdoi2016]数字配对(二分图+费用最大流)

4514: [Sdoi2016]数字配对

Time Limit: 10 Sec   Memory Limit: 128 MB
Submit: 1840   Solved: 703
[ Submit][ Status][ Discuss]

Description

有 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


如果a是b的倍数,且a/b是质数:

①a能分解为奇数个质数相乘,那么a到b+n连一条容量为inf,费用为c[a]*c[b]的边

②a能分解为偶数个质数相乘,那么b到a+n连一条容量为inf,费用为c[a]*c[b]的边

如果a能分解为奇数个质数相乘,那么源点到a连一条容量为sum[a],费用为0的边

如果a能分解为偶数个质数相乘,那么a到汇点连一条容量为sum[a],费用为0的边

这个网络去掉源点汇点及其边后就是一张二分图

之后求出在费用不低于0的情况下的最大流就是答案

改下SPFA

#include<stdio.h>
#include<string.h>
#include<math.h>
#include<stdlib.h>
#include<algorithm>
#include<queue>
using namespace std;
#define LL long long
typedef struct
{
	int x;
	int y, z;
	int tp;
}Res;
Res s[205];
typedef struct
{
	int flow;
	LL cost;
}Edge;
Edge road[505][505];
int dis, T, link[505], vis[505];
LL best[505];
int Spfa();
int Jud(int x)
{
	int i;
	if(x<=1)
		return 0;
	for(i=2;i*i<=x;i++)
	{
		if(x%i==0)
			return 0;
	}
	return 1;
}
int Sech(int x)
{
	int i, sum = 0;
	for(i=2;i*i<=x;i++)
	{
		while(x%i==0)
		{
			sum++;
			x /= i;
		}
	}
	if(x!=1)
		sum++;
	return 2-(sum%2);
}
int main(void)
{
	LL ans, sum;
	int n, x, i, j, pre, flow;
	while(scanf("%d", &n)!=EOF)
	{
		for(i=1;i<=n;i++)
		{
			scanf("%d", &s[i].x);
			s[i].tp = Sech(s[i].x);
		}
		for(i=1;i<=n;i++)
			scanf("%d", &s[i].y);
		for(i=1;i<=n;i++)
			scanf("%d", &s[i].z);
		T = 2*n+1;
		for(i=1;i<=n;i++)
		{
			if(s[i].tp==1)
				road[0][i].flow = s[i].y;
			else
				road[n+i][T].flow = s[i].y;
		}
		for(i=1;i<=n;i++)
		{
			for(j=1;j<=n;j++)
			{
				if(s[i].x%s[j].x==0 && Jud(s[i].x/s[j].x))
				{
					if(s[i].tp==1)
					{
						road[i][n+j].flow = 2147483647;
						road[i][n+j].cost = (LL)s[i].z*s[j].z;
						road[n+j][i].cost = -(LL)s[i].z*s[j].z;
					}
					else
					{
						road[j][n+i].flow = 2147483647;
						road[j][n+i].cost = (LL)s[i].z*s[j].z;
						road[n+i][j].cost = -(LL)s[i].z*s[j].z;
					}
				}
			}
		}
		ans = flow = 0;
		while(Spfa())
		{
			x = T;
			dis = 100000000;
			while(x!=0)
			{
				pre = link[x];
				dis = min(dis, road[pre][x].flow);
				x = pre;
			}
			x = T;
			sum = 0;
			while(x!=0)
			{
				pre = link[x];
				road[pre][x].flow -= dis;
				road[x][pre].flow += dis;
				sum += road[pre][x].cost;
				x = pre;
			}
			if(ans+sum*dis<0)
			{
				flow += -ans/sum;
				break;
			}
			ans += sum*dis;
			flow += dis;
		}
		printf("%d\n", flow);
	}
	return 0;
}

int Spfa()
{
	int i, now;
	memset(link, -1, sizeof(link));
	memset(vis, 0, sizeof(vis));
	memset(best, -62, sizeof(best));
	best[0] = 0;
	queue<int> q;
	q.push(0);
	while(q.empty()==0)
	{
		now = q.front();
		q.pop();
		for(i=1;i<=T;i++)
		{
			if(road[now][i].flow>0 && best[i]<best[now]+road[now][i].cost)
			{
				best[i] = best[now]+road[now][i].cost;
				link[i] = now;
				if(vis[i]==0)
				{
					vis[i] = 1;
					q.push(i);
				}
			}
		}
		vis[now] = 0;
	}
	if(best[T]>=-1e17)
		return 1;
	return 0;
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值