重复覆盖问题的深度优先搜索解决与优化(dfs+迭代加深+A*思想+剪枝)

重复覆盖问题

题目链接(糖果)

题目描述:

糖果店的老板一共有 M 种口味的糖果出售。

为了方便描述,我们将 M 种口味编号 1∼M。

小明希望能品尝到所有口味的糖果。

遗憾的是老板并不单独出售糖果,而是 K 颗一包整包出售。

幸好糖果包装上注明了其中 K 颗糖果的口味,所以小明可以在买之前就知道每包内的糖果口味。

给定 N 包糖果,请你计算小明最少买几包,就可以品尝到所有口味的糖果。

输入格式
第一行包含三个整数 N,M,K。

接下来 N 行每行 K 个整数 T1,T2,⋅⋅⋅,TK,代表一包糖果的口味。

输出格式
一个整数表示答案。

如果小明无法品尝所有口味,输出 −1。

数据范围
1≤N≤100,
1≤M,K≤20,
1≤Ti≤M
输入样例:
6 5 3
1 1 2
1 2 3
1 1 3
2 3 5
5 4 2
5 1 2
输出样例:
2

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
const int N=110,M=1<<20;
int n,m,k;
vector<int>col[N];
int log2[M];
int lowbit(int x)//求最小的1的位置
{
    return x&-x;
}
bool h(int state)//预测,A*的思想,估计任需要的最小的糖果包数
{
    int res=0;
    for(int i=(1<<m)-1-state;i;i-=lowbit(i))
    {
        int c=log2[lowbit(i)];
        res++;
        for(auto row:col[c])i&=~row;//一次挑选选上所有
    }
    return res;
}
bool dfs(int depth,int state)
{
    if(!depth||h(state)>depth)return state==(1<<m)-1;
    int t=-1;
    for(int i=(1<<m)-1-state;i;i-=lowbit(i))
    {
        int c=log2[lowbit(i)];
        if(t==-1||col[t].size()>col[c].size())t=c;//减枝,每次枚举所拥有糖果包数最小的口味
    }
    for(auto row: col[t])
    if(dfs(depth-1,state|row))//继续搜索
    return true;
    return false;
}
int main()
{
    cin>>n>>m>>k;
    for(int i=0;i<m;i++)log2[1<<i]=i;
    for(int i=0;i<n;i++)
    {
        int state=0;
        for(int j=0;j<k;j++)
        {
            int c;
            cin>>c;
            state|=1<<c-1;
        }
        for(int j=0;j<m;j++)
        if(state>>j&1)
        col[j].push_back(state);//记录某种口味有哪些糖果包包含
    }
    for(int i=0;i<m;i++)
    {
        sort(col[i].begin(),col[i].end());
        col[i].erase(unique(col[i].begin(),col[i].end()),col[i].end());//判重
    }
    int depth=0;
    while(depth<=m&&!dfs(depth,0))depth++;//迭代加深优化
    if(depth>m)depth=-1;
    cout<<depth<<endl;
    return 0;
}

用搜索加上优化解决重复覆盖问题,其实也是状压的思想

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值