The 2019 ICPC Asia Shanghai Regional Contest H Tree Partition k、Color Graph

 H题意:

给你一个n个节点n-1条无向边构成的树,每一个节点有一个权值wi,你需要把这棵树划分成k个子树,每一个子树的权值是这棵子树上所有节点权值之和。

你要输出这k棵子树的权值中那个最大的。你需要让输出的结果尽可能小

 

题解:

二分结果,重要的是判断这个二分的值是否满足题目要求

对于划分子树的选择,就选择子树中权值最大且又满足二分的答案的那个子树

 

代码:

 

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e5 + 10;
const int INF = 0x3f3f3f3f;
const long long Max = 1e14 + 10;
int n, k, cnt, flag, head[maxn], num;
ll w[maxn], sum[maxn];
struct Edge
{
    int v, nex;
} e[maxn << 2];
void add_edge(int x, int y)
{
    e[cnt].v = y;
    e[cnt].nex = head[x];
    head[x] = cnt++;
}
void dfs(int x, int fa, ll limit)
{
    sum[x] = w[x];
    for (int i = head[x]; ~i; i = e[i].nex)
    {
        if (!flag)
            return;
        int v = e[i].v;
        if (v != fa)
        {
            dfs(v, x, limit);
            sum[x] += sum[v];
        }
    }
    if (!flag)
        return;
    if (sum[x] > limit)
    {
        vector<ll> vec;
        for (int i = head[x]; ~i; i = e[i].nex)
        {
            int v = e[i].v;
            if (v != fa)
                vec.push_back(sum[v]);
        }
        sort(vec.begin(), vec.end());
        //int len=vec.size();
        while (sum[x] > limit)
        {
            // sum[x]-=vec[len-1];
            // num++;
            // vec.pop_back();
            // len--;
            sum[x] -= vec.back();
            vec.pop_back();
            num++;
        }
        vec.clear();
    }
    if (num > k - 1) //因为我们没有把vec清空,所以最后还需要num数量还需要加1
    {
        flag = 0;
        return;
    }
}
bool check(ll x)
{
    flag = 1;
    num = 0;
    dfs(1, 0, x);
    if (flag)
        return 1;
    else
        return 0;
}
int main()
{
    int t, p = 0;
    scanf("%d", &t);
    while (t--)
    {
        cnt = 0;
        memset(head, -1, sizeof(head));
        memset(sum, 0, sizeof(sum));
        scanf("%d%d", &n, &k);
        for (int i = 1; i < n; ++i)
        {
            int x, y;
            scanf("%d%d", &x, &y);
            add_edge(x, y);
            add_edge(y, x);
        }
        ll l = 1, r = Max, mid, ans;
        for (int i = 1; i <= n; ++i)
        {
            scanf("%lld", &w[i]);
            l = max(l, w[i]);
        }
        while (l <= r)
        {
            mid = (l + r) >> 1;
            if (check(mid))
            {
                r = mid - 1;
                ans = mid;
            }
            else
            {
                l = mid + 1;
            }
        }
        printf("Case #%d: %lld\n", ++p, ans);
    }
    return 0;
}

 

 

K题意:

给你一个由n个节点m条边构成的一个无向图。保证不会出现重边和自环。你需要给边染色,你需要保证给一些边染色之后,图里面不会出现一个奇数环且这个环的所有边都被染色了。问你最多能给多少边染色。

 

题解:

就是二分图的一个性质。

你把这n个点分成两个集合(这一点暴力枚举就可以了,毕竟n<=16),然后如果一条边的两个端点在一个集合就不给它染色,否则就染色

 

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 10 + 10;
const int INF = 0x3f3f3f3f;
struct shudui
{
    int x, y;
} e[1000];
int main()
{
    int t, p = 0;
    scanf("%d", &t);
    while (t--)
    {
        int n, m;
        scanf("%d%d", &n, &m);
        for (int i = 1; i <= m; ++i)
        {
            int x, y;
            scanf("%d%d", &x, &y);
            x -= 1;
            y -= 1;
            e[i].x = x;
            e[i].y = y;
        }
        int sum = 0;
        for (int i = 1; i < (1 << n); ++i)
        //for (int i = 7; i <= 7; ++i)
        {
            int res = 0;
            for (int j = 1; j <= m; ++j)
            {
                int x = e[j].x;
                int y = e[j].y;
                //printf("%d %d %d %d\n", x, y, ((1 << x) & i), ((1 << y) & i));
                if ((((1 << x) & i) > 0 && ((1 << y) & i) == 0) || (((1 << x) & i) == 0 && ((1 << y) & i) > 0))
                {
                    res++;
                }
            }
            // if (res == 5)
            // {
            //     printf("%d***\n", i);
            // }
            sum = max(sum, res);
        }
        printf("Case #%d: %d\n", ++p, sum);
    }
    return 0;
}

 

posted @ 2020-12-11 09:59  kongbursi  阅读(66)  评论(0编辑  收藏  举报