这道题还是用深搜最直接也最简单
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#pragma warning (disable:4996)
using namespace std;
const int maxn = 10000 + 10;
int n, m, a[maxn], flag, flagx;
int s[maxn];
void dfs(int step)
{
if (flagx == 1)
return;
if (step > n)
{
flag++;
if (flag == m + 1)
{
for (int j = 1; j <= n; j++)
printf("%d ", a[j]);
printf("\n");
flagx = 1;
}
return;
}
for (int i = 1; i <= n; i++)
{
if (flag == 0)i = a[step];
if (s[i] == 0)
{
s[i] = 1;
a[step] = i;
dfs(step + 1);
s[i] = 0;
}
}
}
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]);
dfs(1);
return 0;
}
还有一种非常快速的方法,但是有点难理解。(感谢yummy大佬的题解)
我们的目标是把全排列转化成一个变进制数,以方便我们进行加法。
对于第i根手指,它有n-i+1种选择,也就是说,这一位数是n-i+1进制的。
那么,整个过程分为3步:
将火星数变成变进制数
将变进制数加上m
将变进制数变成火星数
我们来看一个实例: 将 1,4,5,2,3变成变进制数
首位1是5种选择{1,2,3,4,5}的第1种,故变为0(从0开始)
次位4是4种选择{2,3,4,5}的第3种,故变为2
中间位5是3种选择{2,3,5}的第3种,故变为2
次低位2是2种选择{2,3}的第1种,故变为0
末位3是1种选择的{3}第1种,故变为0
最后,1,4,5,2,3变成了(02200)_{unknown}(02200)
unknown
然后将它加上3,得(03010)_{unknown}(03010)
unknown
最后将它变回火星数。
首位0表示这位应选择{1,2,3,4,5}第1种,即1
次位3表示这位应选择{2,3,4,5}第4种(1被选过了),即5
中间位0表示这位应选择{2,3,4}第1种,即2
次低位1表示这位应选择{3,4}第2种,即4
末位0表示这位应选择{3}第1种,即3
所以本题答案为“14523”+3=“15243”
#include<iostream>
using namespace std;
int a[10005];
int used[10005] = { 0 };
int m, n;
int main()
{
cin >> n >> m;
for (int i = 1; i <= n; i++) //变进制数
{
cin >> a[i];
int x = a[i];
for (int j = 1; j < a[i]; j++) //看x是所有可以用的数(从小到大)里面的第几个数
x -= used[j]; //比x小的数用过就要-1,相当于x的位置向前走一步
used[a[i]] = 1;
a[i] = x - 1;
}
a[n] += m; //加上要加的数
for (int i = n; i > 0; i--) //进制数的加法
{
a[i - 1] += a[i] / (n - i + 1); //进位给前一位
a[i] %= n - i + 1; //对当前进制取余
}
memset(used, 0, sizeof(used));
for (int i = 1; i <= n; i++) //进制数转原来的数
{
for (int j = 0; j <= a[i]; j++) //怎么变过去的就怎么变回来
if (used[j]) //a[i]前面有小于他的数就+1
a[i]++;
cout << a[i] + 1 << " ";
used[a[i]] = 1;
}
return 0;
}
简单的dfs.
#include<iostream>
#include<cstdio>
using namespace std;
int path[1000]; //存放路径
int serve[1000]; //保持数组
int n;
void dfs(int k)
{
if (k == n+1)
{
for (int i = 1; i<k; i++)
{
printf("%5d", path[i]);
}
cout << endl;
return;
}
for (int i = 1; i <= n; i++)
{
if (serve[i] == 0)
{
serve[i] = 1;
path[k] = i;
dfs(k + 1);
serve[i] = 0;
}
}
}
int main()
{
cin >> n; //输入n的全排列,不允许出现重复的数字
dfs(1);
return 0;
}
这道题因为数据非常小,用枚举就好了,非常简单
枚举思路:(假设n层)
白色1层,蓝色1层,则红色n-2层
白色1层,蓝色2层,则红色n-3层
。。
白色n-2层,蓝色1层,红色1层。
注意每种颜色至少一层
#include<iostream>
#include<cstring>
using namespace std;
int n, m;
char a[100][100];
int main()
{
int min = 0;
int judge = 1;
int w = 0;
int b = 0;
int r = 0;
cin >> n >> m;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
cin >> a[i][j];
for (int i = 1; i <= n - 2; i++) //枚举白色的层数
{
for(int x=1;x<=i;x++) //计算白色
for (int y = 1; y <= m; y++)
{
if (a[x][y] != 'W')
w += 1;
}
for (int j = 1; j <= n - 1 - i; j++) //枚举蓝色
{
for (int x = i + 1; x <= i + j; x++) //计算蓝色
for (int y = 1; y <= m; y++)
{
if (a[x][y] != 'B')
b += 1;
}
for (int x = i + j + 1; x <= n; x++) //计算红色,红色没得选。。它只能被迫接受安排
for (int y = 1; y <= m; y++)
if (a[x][y] != 'R')
r += 1;
if (judge == 1) //第一次计数不用比大小
{
min = r + b + w;
r = b = 0;
judge = 0;
}
else
{
if (min > (r + b + w))
min = r + b + w;
r = b = 0;
}
}
r = b = w = 0;
}
cout << min;
return 0;
}
今天记录到这里