暴力枚举两个数字并判定,如果不能同时选就连边,然后即求最大带权独立集。
普通图的最大带权独立集无法快速求出,但发现两个奇数一定无法使得1式成立(考虑模8,奇数平方模8为1,偶数平方模8为0或4),两个偶数一定无法使2式成立,因此奇数和偶数内部没有边,即形成了一张二分图。
首先假设所有数都可以选,考虑最少要删掉多少:1.将左半部分的点与源点连价值的边;2.将右半部分的点向汇点连价值的边;3.两者之间不能同时选的连一条inf的边。然后跑最小割,这样任意一条inf边的两端必须有一条边被删掉(不选),所以答案就是最大流了。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define N 3005 4 #define inf 0x3f3f3f3f 5 struct ji{ 6 int nex,to,len; 7 }edge[N*N]; 8 queue<int>q; 9 int E,n,ans,a[N],head[N],work[N],d[N]; 10 void add(int x,int y,int z){ 11 edge[E].nex=head[x]; 12 edge[E].to=y; 13 edge[E].len=z; 14 head[x]=E++; 15 if (E&1)add(y,x,0); 16 } 17 int gcd(int x,int y){ 18 if (!y)return x; 19 return gcd(y,x%y); 20 } 21 bool bfs(){ 22 memset(d,-1,sizeof(d)); 23 q.push(0); 24 d[0]=0; 25 while (!q.empty()){ 26 int k=q.front(); 27 q.pop(); 28 for(int i=head[k];i!=-1;i=edge[i].nex) 29 if ((edge[i].len)&&(d[edge[i].to]<0)){ 30 d[edge[i].to]=d[k]+1; 31 q.push(edge[i].to); 32 } 33 } 34 return d[n+1]>=0; 35 } 36 int dfs(int k,int s){ 37 if (k>n)return s; 38 int p; 39 for(int i=head[k];i!=-1;i=edge[i].nex) 40 if ((edge[i].len)&&(d[edge[i].to]==d[k]+1)){ 41 p=dfs(edge[i].to,min(s,edge[i].len)); 42 if (p){ 43 edge[i].len-=p; 44 edge[i^1].len+=p; 45 return p; 46 } 47 } 48 return 0; 49 } 50 int dinic(){ 51 int k,ans=0; 52 while (bfs()){ 53 memcpy(work,head,sizeof(head)); 54 while (k=dfs(0,inf))ans+=k; 55 } 56 return ans; 57 } 58 int main(){ 59 scanf("%d",&n); 60 memset(head,-1,sizeof(head)); 61 for(int i=1;i<=n;i++)scanf("%d",&a[i]); 62 for(int i=1;i<=n;i++)ans+=a[i]; 63 for(int i=1;i<=n;i++) 64 if (a[i]&1)add(0,i,a[i]); 65 else add(i,n+1,a[i]); 66 for(int i=1;i<=n;i++) 67 if (a[i]&1) 68 for(int j=1;j<=n;j++){ 69 if (gcd(a[i],a[j])>1)continue; 70 int c=a[i]*a[i]+a[j]*a[j]; 71 if (c==(int)sqrt(c)*(int)sqrt(c))add(i,j,inf); 72 } 73 printf("%d",ans-dinic()); 74 }