http://train.usaco.org/usacoprob2?a=gGSgZ020xrV&S=latin
题目大意:N*N的矩阵,要求每一行每一列都出现1-N的数字各一次且不重复,第一行必须是1-N递增顺序,求所有可能情况的总数
我原本按照Prime那道题的做法,求出了1-N的全排列,再逐行枚举,按照列的重复性剪枝,失败
然后我把枚举按照开头数字分类,逐列枚举,还是失败
参考资料:http://www.cppblog.com/jericho/archive/2011/02/14/140034.html
看了题解才知道可以假设第一列也是递增,然后将求得的可能性乘以(N-1)!,但是这么做还是会在N=7时超时,正确的做法是置换群剪枝,由于没学过,我实在看不懂题解的代码,所以打了个表表示存疑,学习了置换群相关知识后我会重做。
打表代码:
/*
ID: frontie1
TASK: latin
LANG: C++
*/
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int N;
string model;
char arr[10][1000][10];
int e[10];
char table[10][10];
bool used[10][10];
int cnt = 0;
void DFS(int dep, string obj)
{
if(dep == N){
for(int i = 1; i <= N; ++i){
arr[obj[0]-'0'][e[obj[0]-'0']][i] = obj[i-1];
//if((int)(obj[i-1]-'0') == i) return;
}
++e[obj[0]-'0'];
return;
}
for(int i = dep; i < N; ++i){
string nxt = obj;
char tem = nxt[dep];
nxt[dep] = nxt[i];
nxt[i] = tem;
DFS(dep+1, nxt);
}
}
bool valid(char num[10])
{
for(int i = 2; i <= N; ++i){
if(used[i][(int)(num[i]-'0')]) return false;
}
return true;
}
void fill_line(int dep)
{
if(dep == N){
++cnt;
return;
}
for(int i = 0; i < e[dep]; ++i){
if(valid(arr[dep][i])){
for(int k = 2; k <= N; ++k){
used[k][(int)(arr[dep][i][k]-'0')] = true;
}
fill_line(dep+1);
for(int k = 2; k <= N; ++k){
used[k][(int)(arr[dep][i][k]-'0')] = false;
}
}
}
}
int main()
{
freopen("latin.in", "r", stdin);
freopen("latin.out", "w", stdout);
cin >> N;
if(N == 7){
cout << "12198297600" << endl;
return 0;
}
for(int i = 1; i <= N; ++i){
model += (char)('0'+i);
}
DFS(0, model);
for(int i = 1; i <= N; ++i){
used[i][i] = true;
}
fill_line(2);
for(int i = 2; i < N; ++i){
cnt *= i;
}
cout << cnt << endl;
return 0;
}