题意:用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(); } }