2019西安邀请赛 D. Miku and Generals(二分图染色+背包)

原题地址:https://nanti.jisuanke.com/t/39271

题意:给出n个物品,每个物品有一个权值,要求分成两份,使得两份的权值和最接近,并且有些物品之间有关系,表示不能
在同一份中,问你最后两份中较大的那份是权值和.

思路:由于限制条件,所以我们可以确定有些物品总是对立的,所以我们可以把对立的物品的差值放在背包里面进行dp,每个物品都可以取或不取,所以你都可以加上这个差值或者减去这个差值,我们进行一个可行性背包,找到第一个最小的差值就行了.
因为会出现负数,只需要加上一个大的数就行了.

#include 
#define eps 1e-8
#define INF 0x3f3f3f3f
#define PI acos(-1)
#define lson l,mid,rt<<1
#define rson mid+1,r,(rt<<1)+1
#define CLR(x,y) memset((x),y,sizeof(x))
#define fuck(x) cerr << #x << "=" << x << endl
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int seed = 131;
const int maxn = 3e5 + 5;
const int mod = 1e9 + 7;
const int MX = 1.5e5 + 5;
int T, n, m;
int a[maxn];
struct node {
    int v, w, nxt;
} e[maxn];
int tot, head[maxn];
void add_edge(int u, int v) {
    e[tot].v = v;
    e[tot].nxt = head[u];
    head[u] = tot++;
}
vector<int>V;
int vis[maxn];
vector<int>a1, a2;
void dfs(int u, bool flag) {
    vis[u] = 1;
    if (flag)  a1.push_back(u);
    else a2.push_back(u);
    for (int i = head[u]; ~i; i = e[i].nxt) {
        int v = e[i].v;
        if (vis[v]) continue;
        dfs(v, !flag);
    }
}
int dp[205][maxn];//dp[i][j]前i个物品,差值为j可不可行
int main() {

    scanf("%d", &T);
    while (T--) {
        tot = 0;
        V.clear();
        CLR(vis, 0);
        CLR(head, -1);
        scanf("%d%d", &n, &m);
        int ret = 0;
        for (int i = 1; i <= n; i++) {
            scanf("%d", &a[i]);
            a[i] /= 100;
            ret += a[i];
        }
        int num = ret;
        for (int i = 1; i <= m; i++) {
            int u, v;
            scanf("%d%d", &u, &v);
            add_edge(u, v);
            add_edge(v, u);
        }
        ret = 0;
        for (int i = 1; i <= n; i++) {
            if (!vis[i]) {
                a1.clear();
                a2.clear();
                dfs(i, 0);
                int sum1 = 0;
                int sum2 = 0;
                for (int i = 0; i < a1.size(); i++) {
                    sum1 += a[a1[i]];
                }
                for (int i = 0; i < a2.size(); i++) {
                    sum2 += a[a2[i]];
                }
                int x = abs(sum1 - sum2);
                ret += x;
                if (x != 0)V.push_back(x);
            }
        }
        int sz = V.size();
        for (int i = 1; i <= sz; i++) {
            a[i] = V[i - 1];
        }
        CLR(dp, 0);
        dp[0][MX] = 1;
        for (int i = 1; i <= sz; i++) {
            for (int j = -ret; j <= ret; j++) {
                if (dp[i - 1][j - a[i] + MX]) dp[i][j + MX] = 1;
                if (dp[i - 1][j + a[i] + MX]) dp[i][j + MX] = 1;
            }
        }

        for (int i = MX; i <= MX + ret; i++) {
            if (dp[sz][i]) {
                int x = i - MX;
                printf("%d\n", 100 * ((num - x) / 2 + x));
                break;
            }
        }
    }
    return 0;
}

/*
10 4 1 300 300 100 500 1 2


300
6 4
1000 2000 1000 1500 1000 1500
1 2
2 3
4 5
5 6
*/

你可能感兴趣的:(ACM_DP)