一、介绍:
重复覆盖问题:如下例题对应的图,选取几行,使得每一列都有一个1
精确覆盖问题:如下例题对应的图,选取几行,使得每一列都只有一个1
目前有一个解决这两类问题最快的一个算法:Dancing Links,但是难写难调
二、经典做法:DFS
每一次找到一个未被覆盖的列,枚举所有包含这一列的行,接着递归下一列
三、优化:
1、迭代加深(首先先看有没有选一行的答案,再看有没有选两行的答案......)
2、每次找到选择最少的列(若选了这一行,剩余需要覆盖的列数最少)
3、可行性剪枝(至少选多少行和剩余的行数比较)
4、位运算(同费解的开关)
1和3一起被称作IDA*
例题:
糖果
糖果店的老板一共有 M 种口味的糖果出售。
为了方便描述,我们将 M 种口味编号 1∼M。
小明希望能品尝到所有口味的糖果。
遗憾的是老板并不单独出售糖果,而是 K 颗一包整包出售。
幸好糖果包装上注明了其中 K 颗糖果的口味,所以小明可以在买之前就知道每包内的糖果口味。
给定 N 包糖果,请你计算小明最少买几包,就可以品尝到所有口味的糖果。
输入格式
第一行包含三个整数 N,M,K。
接下来 N行每行 K这整数 T1,T2,⋅⋅⋅,T,代表一包糖果的口味。
输出格式
一个整数表示答案。
如果小明无法品尝所有口味,输出 −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<cstdio>
#include<cstring>
#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)
{
return x&-x;
}
int h(int state)//估价函数,判断最少需要几行
{
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;//标志第c-1位为1
}
for(int j=0;j<m;j++)//将当前状态记录进各列的可选择项中
{
if(state>>j&1)
col[j].push_back(state);
}
}
int depth=0;
while(depth<=m&&!dfs(depth,0)) depth++;
if(depth>m) depth=-1;
cout<<depth<<endl;
return 0;
}