2019ACM-ICPC 西安邀请赛 D.Miku and Generals——二分图染色+01背包


题意

将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

*/
posted @ 2022-09-03 11:12  翔村亲亲鸟  阅读(25)  评论(0编辑  收藏  举报