题目
https://nanti.jisuanke.com/t/39271
题意
给你n个权值 然你分成两组 使他们的权值和的差最小 ,其中有些点是相互矛盾的,不能分在同一组
思路
所有点都是100的倍数,可以直接 除以100
二分图染色 将矛盾的点缩为一个,假设每组小的分为一组,然后交换某些组,就相当于给小的一组加上他们的差值。
如果总和为2x 那么每组为x是最优的,每组小的和为y 那么只要找的小于(x-y)最大的即最优解 01背包即可
#include <bits/stdc++.h>
using namespace std;
struct node
{
int v,nxt;
}e[450];
int head[220];
int vis[220];
int top;
int a[220];
int b[220];
int si;
int dp[100000];
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;
}