2019年ACM-ICPC西安邀请赛DMiku and Generals(二分图+裸0-1背包)

博客讲述了如何解决ACM-ICPC西安邀请赛中的一种问题,该问题涉及将将军卡片分配给两位玩家,使得攻击值的差最小且Miku获胜。通过简化问题为0-1背包问题,利用二分图染色处理矛盾,计算每个元素对总价值的贡献,并应用动态规划求解最大值。

题目链接
“Miku is matchless in the world!” As everyone knows, Nakano Miku is interested in Japanese generals, so Fuutaro always plays a kind of card game about generals with her. In this game, the players pick up cards with generals, but some generals have contradictions and cannot be in the same side. Every general has a certain value of attack power (can be exactly divided by 100 ), and the player with higher sum of values will win. In this game all the cards should be picked up.

This day Miku wants to play this game again. However, Fuutaro is busy preparing an exam, so he decides to secretly control the game and decide each card’s owner. He wants Miku to win this game so he won’t always be bothered, and the difference between their value should be as small as possible. To make Miku happy, if they have the same sum of values, Miku will win. He must get a plan immediately and calculate it to meet the above requirements, how much attack value will Miku have?

As we all know, when Miku shows her loveliness, Fuutaro’s IQ will become 00 . So please help him figure out the answer right now!

Input

Each test file contains several test cases. In each test file:

The first line contains a single integer T ( 1 ≤ T ≤ 10 ) T ( 1 ≤ T ≤ 10 ) (1 \le T \le 10)T(1≤T≤10) (1T10)T(1T10) which is the number of test cases.

For each test case, the first line contains two integers: the number of generals N ( 2 ≤ N ≤ 200 ) N ( 2 ≤ N ≤ 200 ) N(2 \le N \le 200)N(2≤N≤200) N(2N200)N(2N200)and thenumber of pairs of generals that have contradictions⁡ M ( 0 ≤ M ≤ 200 ) M ( 0 ≤ M ≤ 200 ) . M(0 \le M \le 200)M(0≤M≤200). M(0M200)M(0M200).

The second line contains N integers, and the i-th integer is c i c_i ci
, which is the attack power value of the ii-th general ( 0 ≤ c i ≤ 5 × 1 0 4 ) ( 0 ≤ c ≤ 5 × 1 0 4 ) (0 \le c_i \le 5\times 10^4)(0≤c ≤5×10^4) (0ci5×104)(0c5×104)

The following MM lines describe the contradictions among generals. Each line contains two integers AA and BB , which means general A A A and B B B cannot be on the same side ( 1 ≤ A , B ≤ N ) ( 1 ≤ A , B ≤ N ) (1 \le A , B \le N)(1≤A,B≤N) (1A,BN)(1A,BN).

The input data guarantees that the solution exists.

Output

For each test case, you should print one line with your answer.

题意(最终化简成裸的0-1背包问题)

有n个物品,m种关系,每种关系中的物品不能放到一堆当中,问如何将所有物品分到两堆当中,并且要求两堆物品的总价值的差值最小,输出大的那一堆物品的数量
用二分图染色对那些有互斥关系的物品进行处理
处理成一对互斥的点
此时集合中就有几对对互斥的点和几个单独的点
这时我们我们计算每组点的贡献,
如果二分图染色后两个点分别为6,4,如果选择6,那么4一定进入另一堆当中,所以实际上对这一堆的贡献只有|6-4|=2,所以可以这样处理 ,把每一个二分图都处理成两部分的物品的和的差值
单独点的贡献直接就是单独点的权值
然后计算所有贡献的和,用0-1背包计算较小的那一堆的最大值(背包的容量为处理之后的贡献的和的一半)
就能得到两堆的差值,就能计算较大的那一组的权值(即让对手的背包容量最多为贡献的一半,并且让对手尽量装东西,就能保证两边的差值最小)

ac代码
#include<bits/stdc++.h>
using namespace std;
typedef long long int LL;
#define mkp(a,b) make_pair(a,b)
#define pii pair<int,int>
#define mem0(a) memset(a,0,sizeof(a))
#define mem(a,b) memset(a,b,sizeof(a))
const int INF = 0x3f3f3f3f ;
const int maxn = 10000;
const double pi = acos(-1.0);
int T,n,m;
int c[200+5];
int w[maxn*5+100];
vector<int>vec[200+5];
int v[200+5];
LL tot=0;
vector<int>dian;
bool vis[maxn];
int tot1=0;
int tot2=0;
LL sum=0;
int F[201][100000+5];
void dfs(int t,int col)//染色
{
    if(vis[t])return;
    vis[t]=1;
    if(col==1){
        c[t]=0;
        tot1+=w[t];
    }
    else 
    {
        c[t]=1;
        tot2+=w[t];
    }
    int len=vec[t].size();
    for(int i=0;i<len;i++)
    {
        if(vis[vec[t][i]])continue;
        dfs(vec[t][i],c[t]);
    }
}
int main()
{
    cin>>T;
    while(T--)
    {
        mem(vis,0);
        mem(F,0);
        dian.clear();
        tot=0;
        sum=0;
        for(int i=0;i<=200;i++)
        {
            vec[i].clear();
            c[i]=-1;
        }
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&w[i]);
            sum+=w[i]/100;//记录物品的总价值,除以100,因为所有物品都可以整除100
            w[i]/=100;
        }
        int u,v;
        for(int i=1;i<=m;i++)//建立二分图
        {
            scanf("%d%d",&u,&v);
            vec[u].push_back(v);
            vec[v].push_back(u);
        }
        for(int i=1;i<=n;i++)
        {
            if(vis[i]==0)
            {
                tot1=0;
                tot2=0;dfs(i,-1);//染色缩点
                dian.push_back(abs(tot1-tot2));//计算贡献
                tot+=abs(tot1-tot2);//记录贡献的和
            }
        }
        int len=dian.size();
        dian.push_back(dian[0]);
        for (int i = 1; i <= len; i++)//0-1背包板子
            for (int j = 1; j <= tot/2; j++){
                if (j >= dian[i])
                    F[i][j] = max(F[i - 1][j], F[i - 1][j - dian[i]] + dian[i]);
                else
                    F[i][j] = F[i - 1][j];
            }
        LL jian=abs(tot-F[len][tot/2]*2);//计算两堆得差值
        cout<<(sum+jian)/2*100<<endl;//知道两堆的差值和两堆的和,通过方程得到这个结果
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值