D的小L
时间限制:
4000 ms | 内存限制:
65535 KB
难度:
2
-
描述
-
一天TC的匡匡找ACM的小L玩三国杀,但是这会小L忙着哩,不想和匡匡玩但又怕匡匡生气,这时小L给匡匡出了个题目想难倒匡匡(小L很D吧
),有一个数n(0<n<10),写出1到n的全排列,这时匡匡有点囧了
,,,聪明的你能帮匡匡解围吗?
-
输入
- 第一行输入一个数N(0<N<10),表示有N组测试数据。后面的N行输入多组输入数据,每组输入数据都是一个整数x(0<x<10) 输出
-
按特定顺序输出所有组合。
特定顺序:每一个组合中的值从小到大排列,组合之间按字典序排列。
样例输入
-
2 2 3
样例输出
-
12 21 123 132 213 231 312 321
//以下是全排列的基本模板
#include <stdio.h>
int flag[10], a[10], b[10];
int n;
void fun(int x)
{
int i;
if(x == n)//递归出口,如果一次全排列排完了就输出
{
for(i = 0; i < n; i++)
printf("%d", b[i]);
printf("\n");
return ;
}
for(i = 0; i < n; i++)//遍历需要排列的总数
{
if(flag[i] == 0)//现在才明白这里标记的不是数字而是位置,如果当前需要排列的位置没有进行排列赋值则进入赋值递归
{
flag[i] = 1;
b[x] = a[i];
fun(x + 1);
flag[i] = 0;//回溯回来的时候再把该位置标记为没有数
}
}
}
int main()
{
int i, num;
scanf("%d", &num);
for(i = 0; i < 10; i++)//给原始数组赋值,其实好像也可以不用这个数组,直接用下标i来给b赋值也可以,用数组可能改动的时候比较方便吧,只需要改原始数组就可以了
a[i] = i + 1;
while(num--)
{
scanf("%d", &n);
fun(0);
}
return 0;
}
//nyoj32组合数
//组合
组合数
时间限制:
3000 ms | 内存限制:
65535 KB
难度:
3
-
描述
-
找出从自然数1、2、... 、n(0<n<10)中任取r(0<r<=n)个数的所有组合。
-
输入
- 输入n、r。 输出
-
按特定顺序输出所有组合。
特定顺序:每一个组合中的值从大到小排列,组合之间按逆字典序排列。
样例输入
-
5 3
样例输出
-
543 542 541 532 531 521 432 431 421 321
//组合,有点模拟的味道
#include <stdio.h>
int a[10], b[10], flag[10];
int n, m;
void fun(int x)
{
int i;
if(x == m)//递归出口控制好
{
for(i = 0; i < m; i++)
printf("%d", b[i]);
printf("\n");
return ;
}
if(x == 0)//为啥会把要组合的第一个数拿出来呢,这其实就可以相当于是模拟我们组合的时候,总是会先保持哪几个数不变,然后再变哪几个数,这样就可以避免重复
{
for(i = n - 1; i >= m - 1; i--)//因为下标从0开始,所以要-1,其中这里只需要遍历一下前几个数就可以了,至于为啥,自己模拟一下数学上的求组合数就知道啦
{
if(flag[i] == 0)//那么这里我觉得标记的就是数,而不是位置了,标记是否该数有没有被用过
{
flag[i] = 1;
b[x] = a[i];
fun(x + 1);
flag[i] = 0;
}
}
}
else
{
for(i = n - 1; i >= 0; i--)//这里需要遍历原始数组里面的每个数
{
if(flag[i] == 0)
{
if(a[i] < b[x - 1])//这个判断很关键啊,不得不说发明这个的人很牛啊
{
flag[i] = 1;
b[x] = a[i];
fun(x + 1);
flag[i] = 0;
}
}
}
}
}
int main()
{
int i;
for(i = 0; i < 10; i++)
a[i] = i + 1;
while(scanf("%d%d", &n, &m) != EOF)
{
fun(0);
}
return 0;
}
//下面有个简单版本,也比较好理解
#include<stdio.h>
#include <string.h>
#define MAX 0xfffffff
int n, m;
int a[11] = {MAX};
int vis[11];
void dfs(int cur)
{
int k,i;
if(cur == m + 1)
{
for(k = 1; k <= m; k++)
printf("%d", a[k]);
return ;
}
for(i = n; i >= 1;i--)
{
if(!vis[i] && a[cur - 1] > i)//关键应该是这个判断吧
{
a[cur] = i;
vis[i] = 1;
dfs(cur + 1);
vis[i] = 0;
}
}
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
dfs(1);
memset(vis, 0, sizeof(vis));
}
return 0;
}
//nyoj19擅长排列的小明
//排列组合
擅长排列的小明
时间限制:
1000 ms | 内存限制:
65535 KB
难度:
4
-
描述
-
小明十分聪明,而且十分擅长排列计算。比如给小明一个数字5,他能立刻给出1-5按字典序的全排列,如果你想为难他,在这5个数字中选出几个数字让他继续全排列,那么你就错了,他同样的很擅长。现在需要你写一个程序来验证擅长排列的小明到底对不对。
-
输入
-
第一行输入整数N(1<N<10)表示多少组测试数据,
每组测试数据第一行两个整数 n m (1<n<9,0<m<=n)
输出
- 在1-n中选取m个字符进行全排列,按字典序全部输出,每种排列占一行,每组数据间不需分界。如样例 样例输入
-
2 3 1 4 2
样例输出
-
1 2 3 12 13 14 21 23 24 31 32 34 41 42 43
-
第一行输入整数N(1<N<10)表示多少组测试数据,
//这个是排列组合,感觉跟全排列一个样子
#include <stdio.h>
int flag[10], a[10], b[10];
int n, m;
void fun(int x)
{
int i;
if(x == m)//这里控制一下递归出口就好了,至于为什么会这么做,还没有想得特别明白
{
for(i = 0; i < m; i++)
printf("%d", b[i]);
printf("\n");
return ;
}
for(i = 0; i < n; i++)
{
if(flag[i] == 0)
{
flag[i] = 1;
b[x] = a[i];
fun(x + 1);
flag[i] = 0;
}
}
}
int main()
{
int i, num;
scanf("%d", &num);
for(i = 0; i < 10; i++)
a[i] = i + 1;
while(num--)
{
scanf("%d%d", &n, &m);
fun(0);
}
return 0;
}
素数环
时间限制:
1000 ms | 内存限制:
65535 KB
难度:
2
-
描述
-
有一个整数n,把从1到n的数字无重复的排列成环,且使每相邻两个数(包括首尾)的和都为素数,称为素数环。
为了简便起见,我们规定每个素数环都从1开始。例如,下图就是6的一个素数环。
-
输入
- 有多组测试数据,每组输入一个n(0<n<20),n=0表示输入结束。 输出
-
每组第一行输出对应的Case序号,从1开始。
如果存在满足题意叙述的素数环,从小到大输出。
否则输出No Answer。
样例输入
-
6 8 3 0
样例输出
-
Case 1: 1 4 3 2 5 6 1 6 5 2 3 4 Case 2: 1 2 3 8 5 6 7 4 1 2 5 8 3 4 7 6 1 4 7 6 5 8 3 2 1 6 7 4 3 8 5 2 Case 3: No Answer
//nyoj488素数环
//排列的一个变形,这个题目貌似必须要剪枝,不然的话好像是超时,坑啊
#include <stdio.h>
#include <string.h>
#include <math.h>
int a[25], b[25], flag[25];
int n, mark;
int prime[45] = {0,0,1,1,0,1,0,1,0,0,0,1,0,1,0,0,0,
1,0,1,0,0,0,1,0,0,0,0,0,1,0,1,0,0,
0,0,0,1,0,0,0
};
/*int is_prime(int num)//其实我不知道这里用这个函数来判断会不会超时,因为开始我用函数加上没有剪枝提交的超时了,所以我又改成了数组,但是看最优代码好像是用函数写的,不过他只用了一个函数调用
{
int i, flag = 1;
for(i = 2; i <= sqrt(num); i++)
{
if(num % i == 0)
{
flag = 0;
break;
}
}
return flag;
}*/
int check(int i, int x)
{
if(x != n - 1)//这里让我卡了好久,注意这里应该判断的事b数组的位置,而不是a数组的位置
{
if(prime[i + 1 + b[x - 1]] == 1)
return 1;
else
return 0;
}
else
{
if(prime[i + 1 + b[x - 1]] == 1 && prime[i + 1 + 1] == 1)
return 1;
else
return 0;
}
return 0;
}
void dfs(int x)//以下就是全排列的变形啦
{
int i;
if(x == n)
{
mark = 1;
for(i = 0; i < n; i++)
{
printf("%d ", b[i]);
}
printf("\n");
return ;
}
for(i = 1; i < n; i++)
{
if(flag[i] == 0 && check(i, x) == 1)
{
flag[i] = 1;
b[x] = i + 1;
dfs(x + 1);
flag[i] = 0;
}
}
}
int main()
{
int j;
scanf("%d", &n);
j = 0;
while(n != 0)
{
b[0] = 1;//因为题目里面说始终以1开头,所以b的第一个位置必须赋值为1
printf("Case %d:\n", j + 1);
if(n % 2 != 0)//这儿就应该是剪枝了
{
if(n == 1)
printf("1\n");
else
printf("No Answer\n");
}
else
dfs(1);
j++;
scanf("%d", &n);
memset(flag, 0, sizeof(flag));
}
return 0;
}
P1080 N皇后
时间: 1000ms / 空间: 131072KiB / Java类名: Main
描述
检查一个如下的6 x 6的跳棋棋盘,有六个棋子被放置在棋盘上,使得每行、每列只有一个,每条对角线(包括两条主对角线的所有平行线)上至多有一个棋子。
列号
1 2 3 4 5 6
-------------------------
1 | | O | | | | |
-------------------------
2 | | | | O | | |
-------------------------
3 | | | | | | O |
-------------------------
4 | O | | | | | |
-------------------------
5 | | | O | | | |
-------------------------
6 | | | | | O | |
-------------------------
上面的布局可以用序列2 4 6 1 3 5来描述,第i个数字表示在第i行的相应位置有一个棋子,如下:
行号 1 2 3 4 5 6
列号 2 4 6 1 3 5
这只是跳棋放置的一个解。请编一个程序找出所有跳棋放置的解。并把它们以上面的序列方法输出。解按字典顺序排列。请输出前3个解。最后一行是解的总个数。
特别注意: 对于更大的N(棋盘大小N x N)你的程序应当改进得更有效。不要事先计算出所有解然后只输出(或是找到一个关于它的公式),这是作弊。如果你坚持作弊,那么你登陆tyvj的帐号将被无警告删除
列号
1 2 3 4 5 6
-------------------------
1 | | O | | | | |
-------------------------
2 | | | | O | | |
-------------------------
3 | | | | | | O |
-------------------------
4 | O | | | | | |
-------------------------
5 | | | O | | | |
-------------------------
6 | | | | | O | |
-------------------------
上面的布局可以用序列2 4 6 1 3 5来描述,第i个数字表示在第i行的相应位置有一个棋子,如下:
行号 1 2 3 4 5 6
列号 2 4 6 1 3 5
这只是跳棋放置的一个解。请编一个程序找出所有跳棋放置的解。并把它们以上面的序列方法输出。解按字典顺序排列。请输出前3个解。最后一行是解的总个数。
特别注意: 对于更大的N(棋盘大小N x N)你的程序应当改进得更有效。不要事先计算出所有解然后只输出(或是找到一个关于它的公式),这是作弊。如果你坚持作弊,那么你登陆tyvj的帐号将被无警告删除
输入格式
一个数字N (6 <= N <= 13) 表示棋盘是N x N大小的。
输出格式
前三行为前三个解,每个解的两个数字之间用一个空格隔开。第四行只有一个数字,表示解的总数。
测试样例1
输入
6
输出
2 4 6 1 3 5
3 6 2 5 1 4
4 1 5 2 6 3
4
//我又开始了八皇后之路,路漫漫
#include <stdio.h>
int n, cnt;
int print[15];
int visit1[15][15], visit2[15][15], visit3[15][15];//用三个二维数组来分别标记列,主对角线,副对角线
void dfs(int cur)
{
int i;
if(cur == n)
{
cnt++;
if(cnt <= 3)
{
for(i = 0; i < n; i++)
{
printf("%d ", print[i]);
}
printf("\n");
}
return ;
}
for(i = 1; i <= n; i++)
{
if(visit1[0][i] == 0 && visit2[1][cur + i] == 0 && visit3[2][cur - i + n] == 0)//判断一下就好,苦了我好久,我在怀疑自己的智商,我去....
{
print[cur] = i;
visit1[0][i] = 1;
visit2[1][cur + i] = 1;//副对角线
visit3[2][cur - i + n] = 1;//主对角线
dfs(cur + 1);
visit1[0][i] = 0;
visit2[1][cur + i] = 0;
visit3[2][cur - i + n] = 0;
}
}
}
int main()
{
scanf("%d", &n);
dfs(0);
printf("%d\n", cnt);
return 0;
}
棋盘问题
在一个给定形状的棋盘(形状可能是不规则的)上面摆放棋子,棋子没有区别。要求摆放时任意的两个棋子不能放在棋盘中的同一行或者同一列,请编程求解对于给定形状和大小的棋盘,摆放k个棋子的所有可行的摆放方案C。输入含有多组测试数据。 每组数据的第一行是两个正整数,n k,用一个空格隔开,表示了将在一个n*n的矩阵内描述棋盘,以及摆放棋子的数目。 n <= 8 , k <= n 当为-1 -1时表示输入结束。 随后的n行描述了棋盘的形状:每行有n个字符,其中 # 表示棋盘区域, . 表示空白区域(数据保证不出现多余的空白行或者空白列)。 对于每一组数据,给出一行输出,输出摆放的方案数目C (数据保证C<2^31)。
2 1
#.
.#
4 4
...#
..#.
.#..
#...
-1 -1
Sample Output
2 1
有点类似于八皇后问题,但是又稍微比八皇后简单一点,毕竟不需要判断对角线问题,而又比八皇后麻烦一点,毕竟不是始终n==k了,还有k<n的情况考虑,所以又有种排列组合的味道在里面了,所以递归的时候注意处理一下这些特殊状态就好了,注意不能改原地图的,可以用标记数组
#include <stdio.h>
#include <string.h>
int n, k, cnt;
char chess[9][9];
int mark[9][9];
int check(int row, int col)//判断是否该行该列里面有没有放元素
{
int i, flag = 1;
for(i = 0; i < row; i++)
{
if(mark[i][col] == 1)//用标记数组判断就可以了
{
flag = 0;
break ;
}
}
for(i = 0; i < col; i++)
{
if(mark[row][i] == 1)
{
flag = 0;
break ;
}
}
return flag;
}
void dfs(int row, int num)
{
if(num == k)//一次递归完毕
{
cnt++;
return ;
}
if(row == n)//这里是为了判断越界
{
return ;
}
int i;
for(i = 0; i < n; i++)//下面的就有种全排列的味道了
{
if(mark[row][i] == 0 && chess[row][i] == '#' && check(row, i) == 1)//check需要,因为可能一行里面有好几个可以走的棋盘位置
{
mark[row][i] = 1;//表示该点放了一个元素,至于放的是什么东西就没那么重要了,这里一起记录了行数和列数
dfs(row + 1, num + 1);
mark[row][i] = 0;
}
}
dfs(row + 1, num);//这个是为了处理k<n的情况
}
int main()
{
int i, j;
char ch;
while(~scanf("%d%d", &n, &k) && n != -1 && k != -1)
{
for(i = 0; i < n; i++)
{
getchar();
for(j = 0; j < n; j++)
{
scanf("%c", &ch);
if(ch == '#')
{
chess[i][j] = '#';//这里其实做了一点优化,就是让可以走的路才进入棋盘,貌似也不是优化,其实好像效果是一样的
}
}
}
cnt = 0;
dfs(0, 0);
printf("%d\n", cnt);
memset(mark, 0, sizeof(mark));
memset(chess, 0, sizeof(chess));
}
return 0;
}
部分和问题
时间限制:
1000 ms | 内存限制:
65535 KB
难度:
2
-
描述
-
给定整数a1、a2、.......an,判断是否可以从中选出若干数,使它们的和恰好为K。
-
输入
-
首先,n和k,n表示数的个数,k表示数的和。
接着一行n个数。
(1<=n<=20,保证不超int范围)
输出
- 如果和恰好可以为k,输出“YES”,并按输入顺序依次输出是由哪几个数的和组成,否则“NO” 样例输入
-
4 13 1 2 4 7
样例输出
-
YES 2 4 7
-
首先,n和k,n表示数的个数,k表示数的和。
//下面的这个代码超时了
#include<stdio.h>
#include <string.h>
#define MAX 0xfffffff//正数最大值
int n, m, flag;
int a[22] = {MAX}, b[22];
int vis[22];
int is_can(int cur)//我想调用这个函数式导致超时的原因
{
int sum = 0, i;
for(i = 1; i < cur; i++)
{
sum += a[i];
}
if(sum == m)
return 1;
else
return 0;
return 0;
}
void dfs(int cur)
{
int k,i;
if(is_can(cur) == 1)
{
printf("YES\n");
for(k = cur - 1; k >= 1; k--)
printf("%d ", a[k]);
printf("\n");
flag = 1;
return ;
}
for(i = n - 1; i >= 0;i--)
{
if(!vis[i] && a[cur - 1] > b[i])//因为这里是cur-1所以为了避免越界,也为了让数组的第一个元素不影响到判断,所以赋给它一个很大的数
{
a[cur]=b[i];
vis[i]=1;
dfs(cur+1);
vis[i]=0;
}
}
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
int i;
flag = 0;
for(i = 0; i < n; i++)
scanf("%d", &b[i]);
dfs(1);
if(flag == 0)
printf("NO\n");
memset(vis, 0, sizeof(vis));
memset(b, 0, sizeof(b));
}
return 0;
}
//下面是正确代码
#include <stdio.h>
#include <string.h>
int n, k, flag, cnt;
int a[22], b[22], vis[22];
void dfs(int cur)
{
int i;
if(cnt == k)
{
printf("YES\n");
for(i = 0; i < n; i++)
{
if(vis[i] == 1)
printf("%d ", a[i]);
}
printf("\n");
flag = 1;
return ;
}
for(i = cur; i < n; i++)//这儿i是从cur 开始的
{
if(vis[i] == 0 && flag == 0)
{
vis[i] = 1;
cnt += a[i];//这里有个技巧,就是一步一步的加,一步一步的减,就不用调用函数来判断了
dfs(i + 1);
cnt -= a[i];
vis[i] = 0;
}
}
}
int main()
{
while(~scanf("%d%d", &n, &k))
{
int i;
cnt = 0;
flag = 0;
for(i = 0; i < n; i++)
scanf("%d", &a[i]);
dfs(0);
if(flag == 0)
printf("NO\n");
memset(a, 0, sizeof(a));
memset(b, 0, sizeof(b));
memset(vis, 0, sizeof(vis));
}
return 0;
}