AtCoder [ABC310D] Peaceful Teams

注意, n , t ⩽ 10 n,t \leqslant 10 n,t10,这就表明这题可以直接用搜索。

第一步:dfs中需要哪些参数

必不可免的两个:前 x x x 人分成了 t t t 组。
那么怎么记录这些组呢?n很小,t也很小,那么可以让一个数的第 i i i 位代表第 i i i 个人在那一组,即状态压缩。

第二步:考虑剪枝

首先考虑一定完不成的是什么情况,就是剩下的人数 < < < 还需要的组数,这样的话一定凑不满 t t t 组。
还有如果当前组数与要求组数相同就不新建小组了,因为多建小组没有任何意义,不可能是答案中的一种。

第三部:实现

实现很简单,考虑两种情况

  1. 新建小组,那么与它相斥的人不能在这一组中(注意回溯)
  2. 加入原来的小组,要满足第 x x x 人与原来的小组内的每一人不相斥

记录相斥:

for (int i = 1; i <= m; i++)
{
    int u, v;
    cin >> u >> v;
    G[u].push_back(v), G[v].push_back(u);
}

记录与第 x x x 人相斥的人不能在 第 i i i 组:

if (t - tm <= n - x && tm)
    for (int i = 1; i <= tm; i++)
    {
        if (a[i][x]) continue;
        for (int j = 0; j < G[x].size(); j++) a[i][G[x][j]]++;
        num = num * 10 + (i - 1);
       dfs(x + 1, tm, num);
       for (int j = 0; j < G[x].size(); j++) a[i][G[x][j]]--;
       num /= 10;
   }

那么最终代码就得出来了:

#include <iostream>
#include <map>
#include <vector>
using namespace std;
const int NR = 20;
vector<int> G[NR];
int n, t, m, a[NR][NR], ans;
map<long long, bool> vis; 
void dfs(int x, int tm, long long num)
{
    if (x == n + 1 && tm == t && !vis[num])
    {
        vis[num] = true, ans ++;
        return;
    }
    else if (x == n + 1) return;
    if (t - tm <= n - x && tm)
        for (int i = 1; i <= tm; i++)
        {
            if (a[i][x]) continue;
            for (int j = 0; j < G[x].size(); j++) a[i][G[x][j]]++;
            num = num * 10 + (i - 1);
            dfs(x + 1, tm, num);
            for (int j = 0; j < G[x].size(); j++) a[i][G[x][j]]--;
            num /= 10;
        }
    if (tm == t) return;
    tm++;
    for (int i = 0; i < G[x].size(); i++) a[tm][G[x][i]]++;
    num = num * 10 + (tm - 1);
    dfs(x + 1, tm, num);
    for (int i = 0; i < G[x].size(); i++) a[tm][G[x][i]]--;
    num /= 10;
}
int main()
{
    cin >> n >> t >> m;
    for (int i = 1; i <= m; i++)
    {
        int u, v;
        cin >> u >> v;
        G[u].push_back(v), G[v].push_back(u);
    }
    dfs(1, 0, 0);
    cout << ans << '\n';
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值