XJOI 3876 二进制矩阵
题意
定义二进制矩阵为每个元素都是 \(0\) 或者 \(1\) 的矩阵
现在有一个二进制矩阵,但是有些格子缺失了,用'?'表示
现在知道这个矩阵每行的信息
并且知道矩阵每列的信息,但是不知道具体这些列是对应这个矩阵的哪一列
给每个缺失的格子补上后,求满足条件的字典序最小的二进制矩阵
矩阵的字典序为将每行的字符拼接后的字符串代表的字典序
保证有解
输入格式
第一行输入两个整数 \(n,m\) 表示二进制矩阵的行数和列数 \((1≤n,m≤30)\)
接下来 \(n\) 行每行 \(m\) 个字符,第 \(i\) 行描述矩阵的第 \(i\) 行的信息
接下来 \(m\) 行每行 \(n\) 个字符,第 \(i\) 行描述矩阵某一列的信息
输出格式
输出一个字符矩阵表示字典序最小的矩阵
样例1
2 3
10?
?11
01
10
1?
101
011
样例2
3 1
0
?
1
0?1
0
0
1
样例3
2 2
10
01
10
01
10
01
样例4
4 3
??0
11?
?01
1?1
1???
?111
0?1?
010
110
101
101
分析
把'?'变成 \(0\) 肯定比变成 \(1\) 更优,前面的变成 \(0\) 肯定比后面的变成 \(0\) 更优。
设矩阵 \(A\) 为确定行的矩阵,矩阵 \(B\) 为确定列的信息,但不确定列的位置的矩阵。
我们从上到下,从左到右枚举矩阵 \(A\) 中的每一个'?',先假设其为 \(0\) ,检验是否合法,如果合法,那么就把当前的'?'设为 \(0\) ;如果不合法,就把它设为 \(1\) 。然后继续对下面的'?'执行同样操作。
那么如何验证一个'?'的值是否合法?
我们建立二分图,图左侧为 \(i(i\in [1,m])\) ,图右侧为 \(j(j\in [1,m])\) ,枚举 \(A\) 的每一行和 \(B\) 的每一列的信息,如果某行 \(A_i\) 能和某列 \(B_j\) 匹配,就连边 \((i,j)\) 。最后求出二分图的最大匹配,如果等于完美匹配(即匹配边数等于 \(m\) ),那么就合法;否则不合法。
Code
#include<cstdio>
#include<algorithm>
#include<vector>
#define maxn 103
using namespace std;
int n,m,a[maxn][maxn],b[maxn][maxn];
char t[maxn];
namespace BINGRAPH{
vector<int> g[maxn];
int c[maxn];
bool vis[maxn];
bool dfs(int u){
for(int i=0;i<int(g[u].size());i++){
int v=g[u][i];
if(vis[v])continue;
vis[v]=1;
if(!c[v]||dfs(c[v])){
c[v]=u;
return 1;
}
}
return 0;
}
int match(){
int ret=0;
for(int i=m;i>=1;i--){
for(int j=1;j<=m;j++)vis[j]=0;
if(dfs(i))ret++;
}
return ret;
}
void build(){
for(int i=1;i<=m;i++){
g[i].clear();
c[i]=0;
for(int j=1;j<=m;j++){
bool flag=1;
for(int k=1;k<=n;k++){
if(a[k][i]==-1||b[j][k]==-1)continue;
if(a[k][i]!=b[j][k]){flag=0;break;}
}
if(flag)g[i].push_back(j);
}
}
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%s",t+1);
for(int j=1;j<=m;j++){
if(t[j]=='?')a[i][j]=-1;
else if(t[j]=='1')a[i][j]=1;
else a[i][j]=0;
}
}
for(int i=1;i<=m;i++){
scanf("%s",t+1);
for(int j=1;j<=n;j++){
if(t[j]=='?')b[i][j]=-1;
else if(t[j]=='1')b[i][j]=1;
else b[i][j]=0;
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(a[i][j]==-1){
a[i][j]=0;
BINGRAPH::build();
if(BINGRAPH::match()!=m)a[i][j]=1;
}
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
putchar(a[i][j]+'0');
}
putchar('\n');
}
return 0;
}