给定一个 n×m 的01矩阵,每次随机选择任意一个为 1 的格子并将其标记(只是标记,没有改变数值)。现在我们要使整个矩阵每一行、每一列都至少有一个格子被标记。求出期望的步数。
1≤n,m≤20,1≤n×m≤200
1≤n,m≤8
将行和列是否被标记压成二进制状态,然后记忆化搜索来计算期望。
时间复杂度 O(2n+mnm) 。
换一种方式计算答案。
令 Pi 表示经过了 i 步还不能达到目标的概率,易得:
#include <iostream>
#include <cstdio>
using namespace std;
typedef long double db;
const int N=8;
const int D=N<<1;
const int S=1<<D;
int black[100][2];
bool solved[S];
int n,m,tot;
db E[S];
db calc(int sta)
{
if (solved[sta]) return E[sta];
if (sta==(1<<n+m)-1) return solved[sta]=1,E[sta]=0;
int same=0;
db ret=0;
for (int i=1,s;i<=tot;i++)
{
s=sta|(1<<black[i][0]-1)|(1<<black[i][1]+n-1);
if (s==sta) same++;
else ret+=(calc(s)+1.0)/tot;
}
(ret+=(1.0*same)/(1.0*tot))/=((1.0*(tot-same))/(1.0*tot));
return solved[sta]=1,E[sta]=ret;
}
int main()
{
freopen("refuse.in","r",stdin),freopen("refuse.out","w",stdout);
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
{
int x;
scanf("%d",&x);
if (x) black[++tot][0]=i,black[tot][1]=j;
}
calc(0);
printf("%.5lf\n",(double)E[0]);
fclose(stdin),fclose(stdout);
return 0;
}
#include <algorithm>
#include <iostream>
#include <cstdio>
using namespace std;
const int N=14;
const int S=1<<N;
const int M=20;
const int B=200;
typedef long double db;
bool mat[M+5][M+5];
int f[M+5][2][B+5];
int col[M+5];
int n,m,s,p;
int bit[S];
db ans;
inline int lowbit(int x){return x&-x;}
void calc()
{
s=1<<n;
for (int s_=0;s_<s;s_++)
for (int x=s_;x;x-=lowbit(x)) bit[s_]++;
for (int i=1;i<=m;i++)
for (int j=1;j<=n;j++)
if (mat[j][i]) col[i]+=1<<j-1;
for (int sta=0,cnt;sta<s;sta++)
{
cnt=0;
for (int i=1;i<=n;i++)
if ((sta>>i-1)&1)
for (int j=1;j<=m;j++)
cnt+=mat[i][j];
for (int i=1;i<=m;i++)
for (int j=0;j<2;j++)
for (int k=0;k<=p-cnt;k++)
f[i][j][k]=0;
f[1][0][0]=1,f[1][1][bit[~sta&col[1]]]=1;
for (int i=1;i<m;i++)
for (int j=0;j<2;j++)
for (int k=0;k<=p-cnt;k++)
if (f[i][j][k]) f[i+1][j][k]+=f[i][j][k],f[i+1][j^1][k+bit[~sta&col[i+1]]]+=f[i][j][k];
for (int j=0;j<2;j++)
for (int k=0;k<=p-cnt;k++)
if (f[m][j][k]&&cnt+k)
ans+=(((bit[sta]+j)&1)?1:-1)*(1.0*p)/(1.0*(cnt+k))*f[m][j][k];
}
}
int main()
{
freopen("refuse.in","r",stdin),freopen("refuse.out","w",stdout);
scanf("%d%d",&n,&m),p=0;
if (n<=m)
for (int i=1;i<=n;i++)
for (int j=1,x;j<=m;j++)
scanf("%d",&x),p+=(mat[i][j]=x);
else
{
for (int i=1;i<=n;i++)
for (int j=1,x;j<=m;j++)
scanf("%d",&x),p+=(mat[j][i]=x);
swap(n,m);
}
calc();
printf("%.5lf\n",(double)ans);
fclose(stdin),fclose(stdout);
return 0;
}