目录
目录
题意
将n个将军卡片分成两份,要求两份卡片之间的差值尽可能小,求最大的那一份卡片的和,这里有m组卡片是不能放在同一份的
思路
对有矛盾的组我们建图进行01染色,对于每一个连通块得到所有的0点和1点的差值的绝对值,我们存在数组ve中,单点也加入ve中,相当于一份是x一份是0,进行01背包求差值的最小值。这里不能直接输入max(dp[sum/2],sum-dp[sum/2])
.因为求的是差值的绝对值,max(dp[sum/2],sum-dp[sum/2])
不一定是正确答案。
例如样例中我们得到连通块的差值200后,剩下的一个单点700,max(dp[sum/2],sum-dp[sum/2])
输出700并不是正确答案,只是他们的差值500是两份卡片的正确差值(2800-2300)
我们得到差值 d=sum-dp[sum/2]-dp[sum/2]
枚举一下答案即可.
注意:并不能直接开下那么大的数组,因为题目说\(c_i\)都是可以被100整除,那么我们输入的时候都除以100,最后的答案*100即可
代码
点击查看代码
#include <bits/stdc++.h>
#define rep(i, x, y) for (int i = x; i <= y; ++i)
#define per(i, x, y) for (int i = x; i >= y; --i)
#define mem(a, b) memset(a, b, sizeof a)
#define INF 0x3f3f3f3f
#define ll long long
#define pushk push_back
using namespace std;
const int N = 200 + 9;
const int M = 5e5;
int col[N];
int a[N];
int dp[M];
vector<int> g[N];
bool vis[N];
int s1 = 0, s2 = 0, s3 = 0,s=0;
void dfs(int u, int fa)
{
col[u] = col[fa] ^ 1;
vis[u] = 1;
if (!col[u])
s1 += a[u];
else
s2 += a[u];
for (auto &it : g[u])
{
if (vis[it] == 0)
{
dfs(it, u);
}
}
}
int main()
{
int T;
cin >> T;
while (T--)
{
int n, m;
cin >> n >> m;
s1 = s2 = s3 =s= 0;
rep(i, 1, n)
{
cin >> a[i];
g[i].clear(),a[i] /= 100;
vis[i] = 0,s+=a[i];
}
rep(i, 1, m)
{
int u, v;
scanf("%d %d", &u, &v);
g[u].pushk(v), g[v].pushk(u);
}
vector<int> ve;
ve.pushk(0);
rep(i, 1, n)
{
if (vis[i] == 0)
{
s1 = s2 = 0;
dfs(i, 0);
//单点也会被加入这里,相当于s1=0,s2=a[i];
if(abs(s1-s2)) ve.pushk(abs(s1-s2));
s3+=abs(s1-s2);
}
}
rep(j, 0, s3 / 2) dp[j] = 0;
rep(i, 1, ve.size() - 1)
{
per(j, s3 / 2, ve[i])
{
dp[j] = max(dp[j], dp[j - ve[i]] + ve[i]);
}
}
int d = s3-dp[s3/2]*2,ans=0;
rep(i,1,s){
if(s-i-i==d){
ans=s-i;
break;
}
}
cout << 100 *ans << '\n';
}
return 0;
}
/*
2
4 2
1400 700 2100 900
1 3
3 4
4 2
1400 700 2100 900
1 3
3 4
*/