0
点赞
收藏
分享

微信扫一扫

暴力枚举6-8

上古神龙 2022-05-02 阅读 98

在这里插入图片描述

这道题还是用深搜最直接也最简单

#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;
}

今天记录到这里

举报

相关推荐

0 条评论