题意:n个点m条边,每条边的两个端点已知,求构成完全二分图能加的最多的边数;
参考:http://blog.csdn.net/acmhonor/article/details/47072399
思路:并不是一个二分图的题。。。
n个点构成完全二分图是,两边的点数差值最小时边数最多(X+Y=n,求max(x*y));
即在给定一些边的情况下,要求的是将点分在两个集合中数量差最小的情况;
先求出每个联通块中的点的情况,考虑孤立点的个数,通过贪心实现剪枝;
非贪心的情况使用背包,背包容量为n/2,推出最优解;
#include<cstdio> #include<cstring> #include<algorithm> #include<cmath> using namespace std; int t,n,m,top,minnn; int sum1,sum2,num,num1,num2; int sta[2][50010]; int dp[500010]; struct node{ int to,next; }g[500010]; int head[500010],vis[500010]; void add(int u,int v) { g[top].to=v; g[top].next=head[u]; head[u]=top++; } void dfs(int x) { if(vis[x]==-1) vis[x]=0; for(int i=head[x];i!=-1;i=g[i].next) { int v=g[i].to; if(vis[v]==-1) { vis[v]=1-vis[x]; if(vis[v]==0) sum1++; else sum2++; dfs(v); } } } int main() { int i,j,k,a,b; while(scanf("%d",&t)!=EOF) { while(t--) { memset(head,-1,sizeof(head)); memset(vis,-1,sizeof(vis)); scanf("%d%d",&n,&m); top=0;sum1=0;sum2=0; for(i=0;i<m;i++){ scanf("%d%d",&a,&b); add(a,b); add(b,a); } top=0;num=0;num1=num2=0; for(i=1;i<=n;i++) { if(vis[i]==-1) { sum1=1;sum2=0; dfs(i); sta[0][top]=sum1; sta[1][top++]=sum2; if(sum2==0){ num++; } else if(num1<num2){ num1+=max(sum1,sum2); num2+=min(sum1,sum2); } else{ num1+=min(sum1,sum2); num2+=max(sum1,sum2); } } } if(abs(num1-num2)<=num){ printf("%d\n",((n+1)/2)*(n/2)-m); continue; } int minn=-1; for(i=0;i<top;i++) { int ans=0; for(j=n/2;j>=sta[0][i]||j>=sta[1][i];j--){ if(j>=sta[0][i]&&ans<dp[j-sta[0][i]]+sta[0][i]&&dp[j-sta[0][i]]>=minn) //每个联通块必须选一个 ans=dp[j-sta[0][i]]+sta[0][i]; if(j>=sta[1][i]&&ans<dp[j-sta[1][i]]+sta[1][i]&&dp[j-sta[1][i]]>=minn) ans=dp[j-sta[1][i]]+sta[1][i]; dp[j]=ans; if(ans&&ans<=minnn) minnn=ans; } minn=minnn; } printf("%d\n",dp[n/2]*(n-dp[n/2])-m); } } return 0; }
时间: 2024-11-13 23:18:49