hdu 5312 dp(背包)、二分图或其他姿势

时间:2022-07-20 10:28:04

题意:给出一个二分图(不一定连通),问最多能加多少边,使它仍然是二分图

BC周年庆第四题,貌似终判再终判之后数据还是有问题```

据说貌似可以用bitset搞,而且姿势优美是正解```然而我还是用的dp过的

首先就是用黑白染色判断每个区块的两边点的个数,接着因为要边数最多,所以显然要两边点数尽量接近,所以我就用01背包的方法,计算能够得到的 n/2 内的半边最大点数,中间加入已达到的最大值优化和黑白染色得到单点额外记录而不进入背包的优化```然后从TLE变成了200+ms过,只能说出数据的太执着于单点,如果构造出一张全是两点连线的图,大概妥妥爆炸```这个测试样例好鱼```然后就这样“卡”过去了,大概bitset才是真-正解吧```

 #pragma comment(linker, "/STACK:102400000,102400000")
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<math.h>
using namespace std; int head[],nxt[],point[],size=;
int num[];
int c[],dp[]; int max(int a,int b){
return a>b?a:b;
} int min(int a,int b){
return a<b?a:b;
} int read(){
int x=;
char c=getchar();
while(c>''||c<'')c=getchar();
while(c>=''&&c<=''){
x=x*+c-'';
c=getchar();
}
return x;
} void add(int a,int b){
point[size]=b;
nxt[size]=head[a];
head[a]=size++;
point[size]=a;
nxt[size]=head[b];
head[b]=size++;
} void dfs(int a,int x){
c[a]=x;
num[x]++;
for(int i=head[a];~i;i=nxt[i]){
int b=point[i];
if(c[b]==-)dfs(b,!x);
}
} int main(){
int T=read();
while(T--){
int n=read();
int m=read();
memset(head,-,sizeof(head));
size=;
memset(c,-,sizeof(c));
int i,j;
if(m==){
int a=n/;
printf("%d\n",a*(n-a));
continue;
}
for(i=;i<=m;i++){
int a=read();
int b=read();
add(a,b);
}
int cnt=,ans=,maxx=;
memset(dp,-,sizeof(dp));
dp[]=;
int k=;
for(i=;i<=n;++i){
if(c[i]==-){
num[]=num[]=;
dfs(i,);
if(num[]+num[]==){
k++;
continue;
}
for(j=min(n/,maxx+max(num[],num[]));j>=;--j){
if(j-num[]>=&&dp[j-num[]]==cnt){
dp[j]=cnt+;
maxx=max(maxx,j);
ans=max(ans,j);
}
if(j-num[]>=&&dp[j-num[]]==cnt){
dp[j]=cnt+;
maxx=max(maxx,j);
ans=max(ans,j);
}
}
cnt++;
}
}
ans=min(ans+k,n/);
int x1=n-ans;
printf("%d\n",x1*ans-m);
}
return ;
}