BZOJ4514[Sdoi2016]数字配对——最大费用最大流

题目描述

有 n 种数字,第 i 种数字是 ai、有 bi 个,权值是 ci。
若两个数字 ai、aj 满足,ai 是 aj 的倍数,且 ai/aj 是一个质数,
那么这两个数字可以配对,并获得 ci×cj 的价值。
一个数字只能参与一次配对,可以不参与配对。
在获得的价值总和不小于 0 的前提下,求最多进行多少次配对。

输入

第一行一个整数 n。
第二行 n 个整数 a1、a2、……、an。
第三行 n 个整数 b1、b2、……、bn。
第四行 n 个整数 c1、c2、……、cn。

输出

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

样例输入

3
2 4 8
2 200 7
-1 -2 1

样例输出

4

提示

 n≤200,ai≤10^9,bi≤10^5,∣ci∣≤10^5

 

有数量上限、有价值,显然费用流,因为题目要求费用不小于$0$,所以用最大费用最大流。将每个点拆成两个点$i$和$i'$,分别与源点和汇点连边,流量为$b[i]$、费用为$0$。枚举任意两个数判断是否能匹配。因为$i$与$j$能匹配,$j$就能与$i$匹配,所以将$i$与$j'$连边、$j$与$i'$连边,流量为$INF$、费用为$-c[i]*c[j]$(因为跑最大费用最大流,边权取反)。每次$SPFA$找到一条增广路,如果加上之后答案满足要求就继续增广,否则就停止。因为一对数的匹配算了两次,所以最后答案除$2$即可。

#include<set>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<cstdio>
#include<vector>
#include<bitset>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
#define INF 1000000000000000ll
#define inf 1000000000
using namespace std;
int head[1000];
int next[100000];
int to[100000];
ll v[100000];
int c[100000];
int f[1000];
int from[100000];
int tot=1;
int S,T;
ll ans;
int n;
int A[300];
int B[300];
int C[300];
queue<int>q;
int vis[1000];
ll d[1000];
int maxflow;
void add(int x,int y,ll z,int w)
{
	next[++tot]=head[x];
	head[x]=tot;
	to[tot]=y;
	v[tot]=z;
	c[tot]=w;
	from[tot]=x;
	next[++tot]=head[y];
	head[y]=tot;
	to[tot]=x;
	v[tot]=-z;
	c[tot]=0;
	from[tot]=y;
}
bool result()
{
	int now=T;
	int flow=inf;
	while(now!=S)
	{
		flow=min(flow,c[f[now]]);
		now=from[f[now]];
	}
	if(ans+d[T]*flow<=0)
	{
		ans+=d[T]*flow;
		maxflow+=flow;
	}
	else
	{
		maxflow+=fabs(ans)/fabs(d[T]);
		return 1;
	}
	now=T;
	while(now!=S)
	{
		c[f[now]]-=flow;
		c[f[now]^1]+=flow;
		now=from[f[now]];
	}
	return 0;
}
bool SPFA()
{
    for(int i=1;i<=T;i++)
    {
        d[i]=INF;
    }
    d[S]=0;
    q.push(S);
    vis[S]=1;
    while(!q.empty())
    {
        int now=q.front();
        q.pop();
        vis[now]=0;
        for(int i=head[now];i;i=next[i])
        {
            if(!c[i])
            {
                continue;
            }
            if(d[to[i]]>d[now]+v[i])
            {
                d[to[i]]=d[now]+v[i];
                f[to[i]]=i;
                if(!vis[to[i]])
                {
                    q.push(to[i]);
                    vis[to[i]]=1;
                }
            }
        }
    }
    return d[T]!=INF;
}
void find_max()
{
	while(SPFA())
	{
		if(result())
		{
			break;
		}
	}
}
bool check(int x,int y)
{
	if(x<y)
	{
		swap(x,y);
	}
	if(x%y)
	{
		return false;
	}
	int d=x/y;
	for(int i=2;i*i<=d;i++)
	{
		if(d%i==0)
		{
			return false;
		}
	}
	return true;
}
int main()
{
	scanf("%d",&n);
	S=2*n+1,T=S+1;
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&A[i]);
	}
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&B[i]);
		add(S,i,0,B[i]);
		add(i+n,T,0,B[i]);
	}
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&C[i]);
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=i+1;j<=n;j++)
		{
			if(check(A[i],A[j]))
			{
				add(i,n+j,-1ll*C[i]*C[j],1<<30);
				add(j,n+i,-1ll*C[i]*C[j],1<<30);
			}
		}
	}
	find_max();
	printf("%d",maxflow/2);
}
posted @ 2019-03-21 09:57  The_Virtuoso  阅读(230)  评论(0编辑  收藏  举报