Miku and Generals(二分图染色+可行性dp)
题意:
给你n个人,每个人有一个权值 a_i ,(a_i是可以被100整除的))现在需要你将n个人分成两组,有m个关系,a和b有关系代表a和b不能放在同一个组内,为了两组实力尽量平均,要你求两组权值差值最小时最大的值是哪一个
思路:
首先我们知道n个人必须全选分为两组,其次题目保证有解
因此我们很容易想到如果a->b,b->c,那么a一定和c要分在同一组内
这样我们就得到了很多个联通块
#include<cstdio> #include<cmath> #include<iostream> #include<algorithm> #include<cstring> #include<string> #include<stack> #include<queue> #include<vector> #include<map> #include<set> #define ll long long using namespace std; const int MAX=200005,INF = 0x3f3f3f3f; struct node { int v,nxt; }e[450]; int head[220],dp[100000],vis[220],top,a[220],b[220]; int si; void add(int u,int v) { e[top].v=v; e[top].nxt=head[u]; head[u]=top++; } void dfs(int x,int op) { vis[x]=si+op; for(int i=head[x];i!=-1;i=e[i].nxt) { int v=e[i].v; if(vis[v]==0) { if(op==0) dfs(v,1); else dfs(v,0); } } } int main() { int t; scanf("%d",&t); while(t--) { int n,m; scanf("%d%d",&n,&m); int num=0; for(int i=1;i<=n;i++) { scanf("%d",&a[i]); a[i]/=100; num+=a[i]; } memset(head,-1,sizeof(head)); top=0; for(int i=1;i<=m;i++) { int u,v; scanf("%d%d",&u,&v); add(u,v); add(v,u); } memset(vis,0,sizeof(vis)); si=1; for(int i=1;i<=n;i++) { if(vis[i]==0) { dfs(i,0); si+=2; } } si--; memset(b,0,sizeof(b)); int sum=0; for(int i=1;i<=n;i++) { b[vis[i]]+=a[i]; } int p=1; for(int i=1;i<= si;i+=2) { sum+=min(b[i],b[i+1]); b[p++]=abs(b[i]-b[i+1]); } memset(dp,0,sizeof(dp)); m=num/2+num%2; m-=sum; dp[0]=0; for(int i=1;i<p;i++) { for(int j=m;j>=b[i];j--) { dp[j]=max(dp[j],dp[j-b[i]]+b[i]); } } int x=sum; if(dp[m]!= -1) x+=dp[m]; int y=num-x; printf("%d\n",max(x,y)*100); } return 0; }