题意:用n个元素填满一个m行n列的矩阵,使得每行和每列中每个元素仅出现一次,现要再添加一行,使得矩阵仍满足上述限制,问字典序第k大的方案是什么.
解法:错误的解法是填k次,每次填最小的,若仍存在解则第k次的填充方案就是字典序第k小的,例如原有1234,第一次填2143第二次填3412不死后第二小的。
朴素的做法的dfs全排列然后验证,这样显然复杂度太高,于是考虑利用二分匹配剪枝:当递归到第i层,即第i列填充上某个元素x后,若x之后的位置和剩余的元素不能形成完美匹配则不必继续向下递归,这样验证的复杂度为O(VE);
继续简化,模拟一下过程:当i为占用了x,则原来占用x的位置j需要找到另外一个元素y,原来占用y的位置需要找到另外一个元素z。。。直到某个位置找到了i释放的元素,因此就是从j出发找一条增广路,且增广路不能经过左侧小于等于i的点(因为那些已经构成字典序了),若曾在这样一条增广路则说明i位置可以取x,否则直接剪枝,验证的复杂度为O(E)。
import java.util.Arrays;
import java.util.Scanner;
public class Flower3225 {
int maxn = 210, maxm = 40010;
class node {
int be, ne;
node(int b, int e) {
be = b;
ne = e;
}
}
class Edmonds {
int E[][] = new int[maxn][maxn], n, m, len;
int link[] = new int[maxn];
boolean vis[] = new boolean[maxn];
void init(int n, int m) {
this.m = m;
this.n = n;
len = 0;
for (int i = 1; i <= n; i++)
Arrays.fill(E[i], 0);
}
boolean find(int a,int lim) {
if(a<=lim)
return false;
for (int i = 1; i <= m; i++) {
if (E[a][i] == 0 || vis[i])
continue;
vis[i] = true;
if (link[i] == -1 || find(link[i],lim)) {
link[i] = a;
return true;
}
}
return false;
}
int solve() {
Arrays.fill(link, -1);
int ans = 0;
for (int i = 1; i <= n; i++) {
Arrays.fill(vis, false);
if (find(i,0))
ans++;
}
return ans;
}
int lk[]=new int[maxm];
boolean check(int p,int v){
if(link[v]==p)
return true;
for(int i=1;i<=m;i++)
lk[i]=link[i];
for(int i=1;i<=m;i++)
if(link[i]==p)
link[i]=-1;
int temp=link[v];
link[v]=p;
Arrays.fill(vis, false);
if(find(temp,p))
return true;
for(int i=1;i<=m;i++)
link[i]=lk[i];
return false;
}
}
Scanner scan = new Scanner(System.in);
Edmonds hun = new Edmonds();
int map[][] = new int[maxn][maxn], vis[] = new int[maxn],
ans[] = new int[maxn];
int n, m, k, cnt;
void work() {
hun.init(n, n);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
hun.E[i][j] = map[i][j];
Arrays.fill(vis, 0);
cnt = 0;
hun.solve();
if (!dfs(1))
System.out.println(-1);
}
boolean dfs(int d) {
if (d > n) {
cnt++;
if (cnt < k)
return false;
for (int i = 1; i < n; i++)
System.out.print(ans[i] + " ");
System.out.println(ans[n]);
return true;
}
for (int i = 1; i <= n; i++)
if (map[d][i] == 1 && vis[i] == 0 && hun.check(d, i)) {
vis[i] = 1;
ans[d] = i;
if (dfs(d + 1))
return true;
vis[i] = 0;
}
return false;
}
void run() {
int cas = scan.nextInt();
for (int c = 1; c <= cas; c++) {
System.out.print("Case #" + c + ": ");
n = scan.nextInt();
m = scan.nextInt();
k = scan.nextInt();
for (int i = 1; i <= n; i++)
Arrays.fill(map[i], 1);
for (int i = 1; i <= m; i++)
for (int j = 1; j <= n; j++)
map[j][scan.nextInt()] = 0;
work();
}
}
public static void main(String[] args) {
new Flower3225().run();
}
}