有N个正整数,需要从中选出一些数,使这些数的和最大。
若两个数a,b同时满足以下条件,则a,b不能同时被选
1:存在正整数C,使a*a+b*b=c*c
2:gcd(a,b)=1
n<=3000。
解题思路:
将所有点拆成2个
0向i连权为a[i]的边,a[i]向T连权为a[i]的边
有关系的点互相连边,权为inf
答案是tot-ans/2
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
int n,len,T;
int to[500001],zhi[500001],next[500001],h[500001];
int q[6005],dis[6005],a[3001];
inline int read()
{
char y; int x=0,f=1; y=getchar();
while (y<'0' || y>'9') {if (y=='-') f=-1; y=getchar();}
while (y>='0'&&y<='9') {x=x*10+int(y)-48; y=getchar();}
return x*f;
}
int gcd(int x,int y)
{
if (y==0) return x;else return gcd(y,x%y);
}
bool jud(int x,int y)
{
int s=x*x+y*y; int p=sqrt(s);
if (p*p==s && gcd(x,y)==1) return true;
return false;
}
void insert(int x,int y,int z)
{
++len; to[len]=y; zhi[len]=z; next[len]=h[x]; h[x]=len;
}
bool bfs()
{
memset(dis,-1,sizeof(dis)); dis[0]=0;
int tail=1,head=0; q[1]=0;
while (head<tail)
{
++head;
int u=h[q[head]];
while (u!=0)
{
if (zhi[u]>0 && dis[to[u]]==-1)
{
dis[to[u]]=dis[q[head]]+1;
++tail; q[tail]=to[u];
}
u=next[u];
}
}
if (dis[T]==-1) return false; else return true;
}
int dicnic(int now,int sum)
{
if (now==T) return sum;
int sug=0;
int u=h[now];
while (u!=0)
{
if (zhi[u]>0 && dis[to[u]]==dis[now]+1)
{
int s=dicnic(to[u],min(sum-sug,zhi[u]));
if (s)
{
zhi[u]-=s; zhi[u^1]+=s; sug+=s;
if (sug==sum) return sum;
}
}
u=next[u];
}
if (sug==0) dis[now]=-1; return sug;
}
int main()
{
n=read(); T=2*n+1; long long tot=0; len=1;
for (int i=1;i<=n;++i)
{
a[i]=read(); tot+=a[i];
insert(0,i,a[i]); insert(i,0,0);
insert(i+n,T,a[i]); insert(T,i+n,0);
}
for (int i=1;i<=n;++i)
for (int j=1;j<=n;++j)
if (i!=j && jud(a[i],a[j]))
{
insert(i,j+n,0x7fffffff); insert(j+n,i,0);
insert(j,i+n,0x7fffffff); insert(i+n,j,0);
}
long long ans=0;
while (bfs())
{
ans+=dicnic(0,0x7fffffff);
}
printf("%lld",tot-ans/2);
}