5-11 重复拉丁矩阵问题
问题描述
现有 k 种不同价值的宝石,每种宝石都有足够多颗。欲将这些宝石排列成一个 m 行 n列的矩阵,m≤n,使矩阵中每一行和每一列的同一种宝石数都不超过规定的数量。另外还 规定,宝石阵列的第 1 行从左到右和第 1 列从上到下的宝石按宝石的价值最小字典序从小到 大排列。试设计一个算法,对于给定的 k,m 和 n 以及每种宝石的规定数量,计算出有多少 种不同的宝石排列方案。
对于给定的 m,n 和 k,以及每种宝石的规定数量,计算出不同的宝石排列方案数。
数据输入:
第 1 行有 3 个正整数 m,n 和 k,0< m≤ n< 9。第 2 行有k 个数,第 j 个数表示第 j 种宝石在矩阵的每行和每列出现的最多次数。这 k 个数按照宝石 的价值从小到大排列。设这k个数为
,则
。
Java
package Chapter5HuiSuFa;
import java.util.Scanner;
public class ChongFuLaDingJuZhen {
private static int m,n,mm;
private static int[] mv,mu;
private static int[][] board;
private static double count;
public static void main(String[] args){
Scanner input = new Scanner(System.in);
while (true){
count = 0.0;
m = input.nextInt();
n = input.nextInt();
mm = input.nextInt();
mv = new int[mm+1];
mu = new int[n+1];
board = new int[n+1][n+1];
for(int k=1,j=1,t=0; k<=mm; k++){
t = input.nextInt();
mv[k] = t;
while (t > 0){
mu[j++] = k;
t--;
}
}
for(int i=1; i<=n; i++)
for(int j=1; j<=n; j++)
board[i][j] = j;
for(int i=2; i<=n; i++)
swap(board[i],1,i);
backtrack(2,2);
System.out.println((int)count);
}
}
private static void swap(int[] x, int i, int j){
int tmp = x[i];
x[i] = x[j];
x[j] = tmp;
}
private static void backtrack(int r, int c){
for(int i=c; i<=n; i++)
if(ok(r,c,i)){
swap(board[r],c,i);
if(c == n){
if(r == m) count += 1.0;
else backtrack(r+1,2);
}else backtrack(r,c+1);
swap(board[r],c,i);
}
}
private static boolean ok(int r, int c, int s){
int i,j;
int k = board[r][s];
if(s > c)
for(int t=c; t<s; t++)
if(mu[board[r][t]] == mu[k])
return false;
for(i=1,j=0; i<r; i++)
if(mu[board[i][c]] == mu[k])
j++;
if(j > mv[mu[k]]-1)
return false;
else return true;
}
}
Input & Output
4 7 3
2 2 3
84309
5 7 3
1 2 4
285216
Reference
王晓东《计算机算法设计与分析》(第3版)P183