题目链接:https://cn.vjudge.net/problem/Kattis-superdoku
题目大意:给你一个n*n的矩阵,给你这个矩阵的前k行,问你是否能构成矩阵使得每一行每一列都是1~n的排列。(即不考虑对角线的数独)
题目分析:一个标准的二分图匹配问题,会用到匈牙利算法
匈牙利算法的趣味介绍:https://blog.csdn.net/dark_scope/article/details/8880547
在看这个题,既然题目已经帮我们把前k行确定了(如果题目给出的k是1,那么我们就自己设置一个第一行),我们直接从第k+1行开始进行处理,在这里我引用刚才那个趣味介绍里的说法来分析,我们可以把每一列看做是一个男生,1-n个数看做是一个女生,如果一列中之前没有出现数字i,我们可以理解为该行(男生)和数字i(女生)互有好感(显然该列存在数字i就等同没好感了)。
这里在进行匹配时,是以行为单位,从左到右一个个匹配的,和其进行匹配的,是该数字所在列。
net数组相当于girl数组,是数字i匹配对象所在的行数,ans数组是映射数组,帮助我们进行数据的修改。
具体可以看代码了
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int mod=1e9+7; const int maxn=2e5+7; const ll inf=1e18+7; const double pi=acos(-1); int n,k; int h[110][110],l[110][110],a[110][110],vis[110],net[110],ans[110]; bool find(int pos){ for(int i=1;i<=n;i++){ if(l[pos][i]||vis[i])continue; vis[i]=1; if(!net[i]||find(net[i])){ net[i]=pos; ans[pos]=i; return true; } } return false; } bool match(int pos){ memset(net,0,sizeof(net)); int num=0; for(int i=1;i<=n;i++){ memset(vis,0,sizeof(vis)); if(find(i)){ num++; // a[pos][i]=ans[i]; // h[pos][ans[i]]=1; // l[i][ans[i]]=1; } } return num==n;//如果每一列都成功匹配,则返回true } bool solve(){ int num=0; for(int i=k+1;i<=n;i++){ if(match(i)){ num++; } for(int j=1;j<=n;j++){ a[i][j]=ans[j]; h[i][ans[j]]=1; l[j][ans[j]]=1; } } return num==n-k;//k+1之后的行全部成功匹配则返回true } int main(){ scanf("%d%d",&n,&k); memset(h,0,sizeof(h));//二维数组初始化也可以直接用memset,不过fill需要做修改 memset(l,0,sizeof(l)); int flag=0; for(int i=1;i<=k;i++){ for(int j=1;j<=n;j++){ scanf("%d",&a[i][j]); if(++h[i][a[i][j]]>1)flag=1; if(++l[j][a[i][j]]>1)flag=1; } } if(flag) { printf("no\n"); return 0; } if(k==0){//这种情况下我们要自己设一个k for(int i=1;i<=n;i++){ a[1][i]=i; h[1][i]=1; l[i][i]=1; } k=1;//记得将k改为1 } if(!solve()) printf("no\n"); else{ printf("yes\n"); for(int i=1;i<=n;i++){ for(int j=1;j<n;j++) printf("%d ",a[i][j]); printf("%d\n",a[i][n]); } } return 0; }