TopCoder SRM 571 题解

DIV1 250 && DIV2 500:

1- N之内的数,按照字典序排序,取前最多50个。

eg; N = 11, ans = 1, 10, 11, 2, 3, 4, ..., 9

这道题只能模拟,从1开始,往下加0, 一直加到溢出为止, 回退, 再加1, 直到加到末位是0为止:

View Code
1 long long tmp = val * 10;
2     if (tmp <= n) dfs(tmp);
3     else
4     {
5          tmp = val + 1;
6          if (tmp <= n && tmp % 10 != 0) dfs(tmp);
7          else dfs(val / 10 + 1);
8     }

DIV2 1000:

题意:一幅图,N(N <= 50)个节点, M条边(M <= 50 * 51 / 2), 每个节点有一个力量值power[i],要求选出一些节点,满足下列要求:

1. 这些点的数目恰好是K个(K <= 14 && K <= N) 

2. 对于每条边edge[i]两个端点(u, v), 必定至少有一个被选进set中

要求选出满足 condition 1和2的节点power之和的最大值。 覆盖cover类型。

 

上来不敢下手, 递归吧,感觉复杂度挺大的, 大约是C(50, K), 肯定超时。 但是没有从边的角度上思考问题: 枚举每一条边,此时有三种可能,

a. u,v都未被覆盖, 此时需要把u 和 v之间的一个加进去set中; 

b. u, v已经有一个被加到set里了, 此时不做任何处理

c. u 和 v 都在set中, 同b

关键是对于每条边来说, 只满足一个点被覆盖就行。 但是存在一种情况, 就是满足所有边被覆盖的点的个数, 不到K。eg

1-2, 2-3, 3-4   N = 4, K = 3, 选择set(2, 3)或者set(2, 4) 或者 set(3, 1), 都可以覆盖所有边, 但是set.size() < K, 还可以选一个点, 选哪个呢?

此时只要把剩下的没被选的直接排序, 选择前(K - set.size()) 大的即可!贪心ORZ

再看复杂度 : 递归深度最多是K次, 综合 a, b, c, 每次递归做多有两次选择(情况a), 所以复杂度是 O(M * 2 ^ K) MAX(50 * 50 / 2 * 2 ^ 14) = 20480000

View Code
#include <iostream>
#include <string>
#include <string.h>
#include <vector>
#include <map>
#include <set>
#include <queue>
#include <math.h>
#include <algorithm>
#include <stdio.h>
using namespace std;

vector<int> magicPower;
vector<string> magicBond;
struct node
{
    int x, y;
    node(){}
    node(int _x, int _y){x = _x; y = _y;}
};
vector<node>edge;

int n, k;
int ans = -1;
int m = 0;
void dfs(int id, long long state, int select)
{
    int i, j;
    int val = 0;
    if (select > k) return;
    if (id == m)
    {
        vector<int>v;
        for (i = 0; i < n; i++)
        {
            if ( (state >> (long long)i) & 1) val += magicPower[i];
            else v.push_back(magicPower[i]);
        }
        cout << select << "\t";
        sort(v.begin(), v.end());
        reverse(v.begin(), v.end());
        for (i = 0; i < k - select && i < v.size(); i++)
        {
            val += v[i];
        }
        ans = max(ans, val);
        cout << ans << endl;
        return;
    }
    int start = edge[id].x;
    int end = edge[id].y;
    if (  !((state >> (long long)start) & 1L) && !((state >> (long long)end) & 1L) )
    {
        dfs(id + 1, state | (1LL << start), select + 1);
        dfs(id + 1, state | (1LL << end), select + 1);
    }
    else dfs(id + 1, state, select);
}

class MagicMoleculeEasy
{
public:
    int maxMagicPower(vector<int> _magicPower, vector<string> _magicBond, int _k)
    {
        k = _k;
        magicPower = _magicPower;
        magicBond = _magicBond;
        n = magicPower.size();
        int i, j;
        for (i = 0; i < _magicBond.size(); i++)
        {
            for (j = i + 1; j < _magicBond[0].size(); j++)
            {
                if (_magicBond[i][j] == 'Y') edge.push_back(node(i, j));
            }
        }
        m = edge.size();
        dfs(0, 0, 0);
        return ans;
    }
};

 

DIV1 500:

同上, 只不过条件1和2变化了:

1. 选出来的点是K个, 满足3 * K >= 2 * N, 即 K >= 2 / 3 * N

2. 对于 每个在集合中连个节点i, j, i和j有一条边相连

考虑到 K >= 2 / 3 * N, 所以如果像上面那样递归, 肯定会超时。 根据tc的官方题解, 此时如果换个角度思考问题: 最多拿出 N / 3 (50 / 3 = 17.X)的点,  使得剩下的任意的两点满足2.

问题是, 要拿出哪些点 ? 考虑到条件2, 假如 (i , j) 没有边连接, 那么i和j至少要删除一个。 

此时问题就可以简化为, 在图中至多选取K个点, 其中 (K < N / 3), 对于图中每一对(i, j), 其中i和j没有边连接, 至少i或j被选取。

View Code
#include <iostream>
#include <string>
#include <string.h>
#include <vector>
#include <map>
#include <set>
#include <queue>
#include <math.h>
#include <algorithm>
#include <stdio.h>
using namespace std;

vector<int> magicPower;
vector<string> magicBond;
struct node
{
    int x, y;
    node(){}
    node(int _x, int _y){x = _x; y = _y;}
};
vector<node>edge;

int n;
int ans = -1;
int m = 0;
void dfs(int id, long long state, int select)
{
    int i, j;
    int val = 0;
    if (select * 3 > n) return;
    if (id == m)
    {
        vector<int>v;
        for (i = 0; i < n; i++)
        {
            if ( !((state >> (long long)i) & 1) ) val += magicPower[i];
        }
        ans = max(ans, val);
        cout << ans << endl;
        return;
    }
    int start = edge[id].x;
    int end = edge[id].y;
    if (  !((state >> (long long)start) & 1L) && !((state >> (long long)end) & 1L) )
    {
        dfs(id + 1, state | (1LL << start), select + 1);
        dfs(id + 1, state | (1LL << end), select + 1);
    }
    else dfs(id + 1, state, select);
}

 

 

转载于:https://www.cnblogs.com/kaitian521/archive/2013/05/04/3060268.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值