[Acwing蓝桥杯搜索] 1243. 糖果

题目链接:1243. 糖果 - AcWing题库

题目大意:有n列m行 每行有k个数(k可能重复) 求最少选多少行可以覆盖所有的列

数据范围:1<=n<=100,1<=m,k<=20

数据范围非常小 最多有20列 枚举这20列可以用状态压缩

题目分析:

这是个重复覆盖问题。

重复覆盖指的是:列可以重复选,最后结果 一个列可能选多次

精确覆盖指的是:列不可以重复选,最后结果 一个列只能选一次 ,比如说:数独

最快做法:Dancing links(比较难,不会)
这个题用比较好写的写法:暴搜求最小值
1、搜索顺序
以此找到所有未被覆盖的列 所满足的行 直到所有的列都覆盖掉
或者某列 无法满足 无解
2、剪枝优化
(1)迭代加深 答案是1、答案是2... 一个个枚举判断
(2)找选择行最少的列
(3)可行性剪枝 采用估价函数
(4)将重复的行删掉
估价函数 h( )当前至少要选多少行才可以覆盖所有列  是个极限的情况
h( )与剩下的行比较 若h( )大于剩下的行 无解
(1)和(3)合称:IDA*优化

代码如下:

 

复制代码
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;

const int N=110,M=1<<20;

int n,m,k;
vector<int>cols[N];//cols[i]存i列为1的所有行的状态,即行状态含有第i列
int log2[M];//初始化log2数组 ,ps:如果用万能头就不能这样命名了 
//和lowbit函数配合使用 求出最后一个所在列 需要初始化

int lowbits(int x)
{
    return x&-x;
}

//估价出 最少的极限情况
int h(int state)
{
    int res=0;
    for(int i=(1<<m)-1-state;i;i-=lowbits(i))
    {
        int c=log2[lowbits(i)];    
        res++;
        //由于求极限 就要把所有含有c列的行都包含进去
        //这里是细节技巧,由于状态i中还选的列相应的压缩状态为1
        //row中的已经选的列压缩状态为1
        //要将row中相应的列在状态i中标为0,表示已经选了
        //可以将row先取反再与i相与
        //row中的1取反变为0与i相与将i中的该位也变为0
        //row中的0取反变为1与i相与不改变i中位的状态
        for(auto row:cols[c])i&=~row;
    }
    return res;
}

bool dfs(int depth,int state)
{
    //如果没有可选择的深度或者深度小于估价出的最小的方案数,直接判断返回是否覆盖完   
    if(!depth||h(state)>depth)return state==(1<<m)-1;
    
    //找出选择行最少的列t
    int t=-1;
    for(int i=(1<<m)-1-state;i;i-=lowbits(i))
    {
        //依次枚举出所有的未被选择的列
        int c=log2[lowbits(i)];
        if(t==-1||cols[t].size()>cols[c].size())
        {
            t=c;
        }
    }
    
    //找出列后依次dfs出该列的所有行的情况
    for(auto row : cols[t])
        if(dfs(depth - 1, state | row))
            return true;
    
    return false;
}

int main()
{
    cin>>n>>m>>k;
    
    //初始化log2数组
    for(int i=0;i<m;i++)log2[1<<i]=i;
    
    for(int i=0;i<n;i++)
    {
        int state=0;
        //存state的k个列所在位置
        for(int j=0;j<k;j++)
        {
            int c;
            cin>>c;
            state|=1<<c-1;//状态压缩 从0开始,下标减1
        }
        
        //将state存放在所对应的列后边
        for(int j=0;j<m;j++)
        {
            if(state>>j&1)
            {
                cols[j].push_back(state);
            }
        }
    }
    
    //剪枝:将列的 重复的行删掉
    for(int i=0;i<m;i++)
    {
        sort(cols[i].begin(),cols[i].end());
        cols[i].erase(unique(cols[i].begin(),cols[i].end()),cols[i].end());
    }
    
    int depth=0;//枚举答案 也就是迭代加深
    while(depth<=m&&!dfs(depth,0))depth++;
    
    if(depth>m)depth=-1;
    cout<<depth<<endl;
    
    return 0;
}
复制代码

 

end!!!

 

posted @   秦末  阅读(27)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
1 博文导航目录
点击右上角即可分享
微信分享提示