avatar
fireworks99
keep hungry keep foolish

Codeforces B.Arpa's weak amphitheater and Mehrdad's ...

Description

n人分属不同的朋友圈,这些人都有自己的体重w与美丽度b,他们之间有m个关系描述(是朋友)。有一个舞台最大承重w,从每个朋友圈中选一个人或全选,在不超重前提下选美丽度最大的情况。

http://codeforces.com/problemset/problem/741/B

思路

题目有个关于两个人属于同一朋友圈的叙述:

Two Hoses x and y are in the same friendship group if and only if there is a sequence of Hoses a1, a2, …, ak such that ai and ai + 1 are friends for each 1 ≤ i < k, and a1 = x and ak = y.

AB、BC属于同一个圈,则AC属于同一个圈

AB、BC、CD属于同一个圈,则AD属于同一个圈

仔细想想其实就是个并查集

选一个或‘全都要’:

将’全都要‘等效成新的‘一个’,即将‘特殊’一般化

接下来背包DP,不过我没看出来‘每组只取一个’体现在哪?重量循环在外层?可以问问师哥

Code

#include <cmath>
#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

int N, M, W, x, y;
int w[10005];
int b[10005];
int pre[10005];
int dp[1005];
vector<int> vec[10005];

void init()
{
    for(int i = 0; i <= N; ++i)
        pre[i] = i;
}

int found(int x)
{
    if(x != pre[x])
        pre[x] = found(pre[x]);
    return pre[x];
}

void unite(int a, int b)
{
    int x = found(a);
    int y = found(b);
    if(x != y)
        pre[y] = x;
}

int main()
{
    cin >> N >> M >> W;
    ///1. 初始化与输入
    init();
    for(int i = 1; i <= N; ++i)
        scanf("%d", &w[i]);
    for(int i = 1; i <= N; ++i)
        scanf("%d", &b[i]);

    ///2. 并查集分组
    for(int i = 1; i <= M; ++i)
    {
        scanf("%d%d", &x, &y);
        unite(x, y);
    }
    for(int i = 1; i <= N; ++i)
        vec[ found(i) ].push_back(i);

    ///3. 枪打出头鸟:特殊一般化
    ///"全拿"等效成"新的一组"
    int num = N + 1;
    for(int i = 1; i <= N; ++i)
    {
        if(vec[i].size() == 0)
            continue;

        int sz = vec[i].size();
        int ww = 0, bb = 0;
        for(int j = 0; j < sz; ++j)
        {
            ww += w[ vec[i][j] ];
            bb += b[ vec[i][j] ];
        }
        w[num] = ww;
        b[num] = bb;
        vec[i].push_back(num++);
    }

    for(int i = 1; i <= N; ++i)///某个非空集合num
    {
        if(vec[i].size() == 0)
            continue;

        ///哪里体现了"从每个集合里只选一个"的要求?
        int sz = vec[i].size();
        for(int j = W; j >= 1; --j)
            for(int k = 0; k < sz; ++k)///非空集合num中每个元素
                if(j >= w[ vec[i][k] ])/// >= 别漏了'='
                    dp[j] = max(dp[j], dp[j - w[ vec[i][k] ]] + b[ vec[i][k] ]);
        ///每个集合循环一次才决定了一个dp[j],每个集合只对应一个dp[w]
        ///最终的dp[w]是最优的
    }
    cout << dp[W] << '\n';
    return 0;
}
Site by Baole Zhao | Powered by Hexo | theme PreciousJoy