PAT顶级(Bron–Kerbosch算法优化最大团DFS)——1021 Safe Fruit (35 分)

1021 Safe Fruit (35 分)


解题思路:

用纯DFS+剪枝可以剪到26分。。。最后两个点不这样估计是过不去了,最大团(最大独立集)问题的裸题。
原来的搜索方法:
顺序搜索,从0(位置)开始枚举每个点,先根据已有的集合判断这个点可不可以被选,如果这个点可以被挑选,则进行选、不选的两个状态搜索,如果不能,则直接进行不选的状态搜索。
尝试记忆化搜索,内存超限,失败。
(贪心可以过最后一个点,再拿3分)
学习Bron–Kerbosch算法之后的搜索优化:

  1. 转变思路,将题目没有列举出来的边视为相连边,列举出来的视为不相连的边。
  2. 倒序枚举每个点作为起始状态(即这个点必选),然后从这个点往后进行正常的最大团搜索(即新加的点必须与所有已选的点相连),就可以得到以这个点为起点往后进行搜索的最大团,同时我们不重置以往搜索到的最大团,就可以得到从i位置往后任何一个位置为起点的最大团(即后缀最大团),后缀最大团个数记为cnt[i]。
  3. 进行剪枝,在每次选新的点i的时候,因为是向后搜索,所有cnt[i]都会已经被计算过,因此我们判断cnt[i]+num即当前的理想最优情况(可能会更差,因为加入了前面的点,可能会冲突无法取到cnt[i])是否比max要小,如果cnt[i]+num<max,说明取了点i的理想最优情况下都不如我的已有答案要好,那显而易见直接return;

就是这样短短的优化,最后只需要200ms,可见还是剪了很多情况的,有点类似DP的思想

代码:

#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
#define ll long long
#define pii pair<int,int>
#define pll pair<ll,ll>
#define mp make_pair
#define pb push_back
#define G 6.67430*1e-11
#define  rd read()
#define pi 3.1415926535
using namespace std;
const ll mod = 1e9 + 7;
const int MAXN = 30000005;
const int MAX2 = 300005;
inline ll read() {
    ll x = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch>'9') {
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') {
        x = (x << 1) + (x << 3) + (ch ^ 48);
        ch = getchar();
    }
    return x * f;
}
ll fpow(ll a, ll b)
{
    ll ans = 1;
    while (b)
    {
        if (b & 1)ans = ans * a % mod;
        b >>= 1;
        a = a * a % mod;
    }
    return ans;
}
int fa1[1005], fa2[1005];
int find(int* fa, int r) { return fa[r] == r ? r : fa[r] = find(fa, fa[r]); }
void join(int* fa, int x, int y)
{
    int fx = find(fa, x), fy = find(fa, y);
    fa[fx] = fy;
}
#define int ll
ll gcd(ll a, ll b) { return b == 0 ? a : gcd(b, a % b); }
int g[1000][1000];
int price[1000];
vector<int> con[1000];
bool cmp(int a, int b)
{
    return con[a] < con[b] || (con[a] == con[b] && price[a] < price[b]);
}
vector<int> v, ans, tmp;
int enable[1000];
int cnt[1000];
int n, m, ma = 0, miprice = 1e9;
void dfs(int i, int num, int p)
{
    
    for (int j = i + 1; j < m; j++)
    {
        if (cnt[j] + num < ma)return;
        if (!g[v[i]][v[j]])
        {
            int f = 0;
            for (auto p : tmp)
            {
                if (g[v[j]][p])
                {
                    f = 1;
                    break;
                }
            }
            if (!f)
            {
                tmp.push_back(v[j]);
                dfs(j, num + 1, p + price[v[j]]);
                tmp.pop_back();
            }
        }
    }
    if (num > ma || (num == ma && p < miprice))
    {
        ma = num;
        ans = tmp;
        miprice = p;
    }
}
signed main()
{
    n = rd, m = rd;
    for (int i = 0; i < n; i++)
    {
        int a = rd, b = rd;
        g[a][b] = g[b][a] = 1;
    }
    for (int i = 0; i < m; i++)
    {
        int a = rd, b = rd;
        price[a] = b;
        v.push_back(a);
    }
    for (int i = m - 1; i >= 0; i--)
    {
        tmp.push_back(v[i]);
        dfs(i, 1, price[v[i]]);
        cnt[i] = ans.size();
        tmp.pop_back();
    }
    sort(ans.begin(), ans.end());
    cout << ans.size() << endl;
    int f = 1;
    for (auto p : ans)
    {
        if (!f)cout << ' ';
        printf("%03d", p);
        f = 0;
    }
    cout << endl << miprice;
    return 0;
}
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值