Codeforces Lucky Permutation Triple
构造类问题,打表找规律
题意:输入n,生成一个n的全排列a,生成一个n的全排列b,从第1位到第n位,每个位上的对应的(ai+bi)%n=ci,然后得到n位的序列c,看看c是不是也是一个n的全排列,是的话就成功了,输出。如果无论怎样都找不到合适的a和b去构造出c,那么就输出-1
这题一想通了,仅是那么的水,但是自己还是很长时间去思考
说说思考的过程
1.很容易想到,题目是想我们我们构造一个合适的a,b,进而产生合适的c,虽然题目让我们输出任意一个合法的排列就可以了,但其实要我们找的不是排列方案,而是匹配方案
试想一下,如果手头上有合适的一个排列,那么我们把对应的ai,bi,ci捆版在一次,我们完全可以对这个排列进行重新排列(重新排列时,捆版的元素要一起移动),这样我们可以重新排除n!个排列,都是合法的,因为对应位在一起。
所以我们得到一个结论,要么无解,要是有解的话,解的个数至少为n!(对于n=1也成立,n=1比较特殊,就是0 0 0),因为可能存在多种匹配方案
2.有上面的结论远远不够解题,这时候我就想用暴力先打表看看当n较小的时候会有什么规律可循,但是没有电脑就一直手写。终于推到8的时候,发现n为偶数的时候都是无解,后来用电脑暴力打表也证实了这个规律
3.但是对于n为奇数的情况,还是没有一个很好的解决方案,最终还是靠电脑打表,才发现了这是个水题,只要a和b都是0 1 2 3 4……n-1的时候,就必然是一个可行解
所以就这样解决了
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; #define N 100010 int main() { int n; cin >> n; if(!(n&1)) cout << -1 << endl; else { for(int i=0; i<n; i++) cout << i << " "; cout << endl; for(int i=0; i<n; i++) cout << i << " "; cout << endl; for(int i=0; i<n; i++) cout << (2*i)%n << " "; cout << endl; } return 0; }
打表程序
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; #define N 100010 int a[N],b[N],c[N]; bool ok,usedb[N],usedc[N]; void dfs(int n,int cur) { if(cur >= n) { ok =true; cout << "可行解" << endl; for(int i=0; i<n; i++) cout << a[i] << " "; cout << endl; for(int i=0; i<n; i++) cout << b[i] << " "; cout << endl; for(int i=0; i<n; i++) cout << c[i] << " "; cout << endl; return ; } for(int i=0; i<n; i++) if(!usedb[i] && !usedc[ (a[cur]+i)%n ]) { usedb[i] = usedc[ (a[cur]+i)%n ] = true; b[cur] = i; c[cur] = (a[cur]+i)%n; dfs(n,cur+1); usedb[i] = usedc[ (a[cur]+i)%n ] = false; } } void solve(int n) { memset(usedb,false,sizeof(usedb)); memset(usedc,false,sizeof(usedc)); for(int i=0; i<n; i++) a[i] = i; dfs(n,0); if(!ok) cout << "无解" << endl; } int main() { freopen("BF.txt","w",stdout); for(int n=1; n<=10; n++) { printf("%d:_______________\n",n); solve(n); cout << endl; } return 0; }