算法——杂谈算法(OJ测试已通过)

之前的题目都是给了分类的,但是这次因为拖得时间久了,听老师上课又一脸地铁老人看手机.jpg

加上对这方面的热情有点退潮,看隔壁网页开发那张小脸越看越喜欢,但又放不下算法这段深刻感情,所以还是总结一把,毕竟也得体体面面好聚好散。

题一 罗密欧与朱丽叶的迷宫

罗密欧与朱丽叶身处一个 m×n 的迷宫中。每一个方格表示迷宫中的一个房间。这 m×n 个房间中有一些房间是封闭的,不允许任何人进入。在迷宫中任何位置均可沿 8 个方向进入未封闭的房间。罗密欧位于迷宫的(p,q)方格中,他必须找出一条通向朱丽叶所在的(r,s)方格的路。在抵达朱丽叶之前,他必须走遍所有未封闭的房间各一次,而且要使到达朱丽叶的转弯次数为最少。每改变一次前进方向算作转弯一次。请设计一个算法帮助罗密欧找出这样一条道路 。

对于给定的罗密欧与朱丽叶的迷宫,编程计算罗密欧通向朱丽叶的所有最少转弯道路。

输入

第一行有 3 个正整数 n,m,k,分别表示迷宫的行数,列数和封闭的房间数。接下来的 k 行中,每行 2 个正整数,表示被封闭的房间所在的行号和列号。最后的 2 行,每行也有 2 个正整数,分别表示罗密欧所处的方格(p,q)和朱丽叶所处的方格(r,s)。 

输出

将计算出的罗密欧通向朱丽叶的最少转弯次数和有多少条不同的最少转弯道路输出。第一行是最少转弯次数。第2行是不同的最少转弯道路。接下来的 n 行,每行 m 个数,表示迷宫的一条最少转弯道路。A[i][j]=k 表示第k步到达方格 (i, j);A[i][j]=-1 表示方格(i,j)是封闭的。

如果罗密欧无法通向朱丽叶则输出 “No Solution!”

样例输入

3 4 2(3行4列2个封闭房间)

1 2(第一个封闭房间坐标)

3 4(第二个封闭房间坐标)

1 1(罗密欧房间坐标)

2 2(朱丽叶房间坐标)

样例输出

6(最少转弯次数)

7(不同的最少转弯道路)

1 -1 9 8

2 10 6 7

3 4 5 -1

#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;

int n, m, k;
int map[105][105];
int path[105 * 105];
int best[105 * 105];

int dx[] = { 0,0,1,-1,1,1,-1,-1 };
int dy[] = { 1,-1,0,0,1,-1,-1,1 };
int p, q, r, s;
int minturn = 0x3f3f3f3f;
int num = 0;

void backtrack(int x, int y, int cur) {
    if (cur >= n * m - k) {
        if (x != r || y != s)  return;
        int turn = 0;
        for (int i = 2; i < cur; i++)
            if (path[i] != path[i - 1])   turn++;
        if (turn < minturn) {
            memcpy(best, path, sizeof(path));
            minturn = turn;
            num = 1;
        }
        else if (turn == minturn)   num++;
        return;
    }
    for (int i = 0; i < 8; i++) {
        int xx = x + dx[i];
        int yy = y + dy[i];
        if (map[xx][yy] == -1)  continue;
        if (xx<1 || xx>n || yy<1 || yy>m)  continue;
        path[cur] = i;
        map[xx][yy] = -1;
        backtrack(xx, yy, cur + 1);
        map[xx][yy] = 0;
    }
}

void path_solve() {
    int x = p, y = q;
    map[p][q] = 1;
    for (int i = 1; i < n * m - k; i++) {
        int xx = x + dx[best[i]];
        int yy = y + dy[best[i]];
        map[xx][yy] = map[x][y] + 1;
        x = xx;
        y = yy;
    }
}

int main() {
    cin >> n >> m >> k;
    for (int i = 0; i < k; i++) {
        int x, y;
        cin >> x >> y;
        map[x][y] = -1;
    }
    cin >> p >> q >> r >> s;
    map[p][q] = -1;
    backtrack(p, q, 1);

    if (minturn == 0x3f3f3f3f) {
        cout << "No Solution!" << endl;
    }
    else {
        path_solve();
        cout << minturn << endl << num << endl;
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= m; j++) {
                if (j != 1)   cout << " ";
                cout << map[i][j];
            }
            cout << endl;
        }
    }

    return 0;
}

题二 工作分配给 n 个人问题

题目描述

设有 n 件工作分配给 n 个人。将工作 i 分配给第 j 个人所需的费用为cij 。试设计一个算

法,为每一个人都分配 1 件不同的工作,并使总费用达到最小。 

设计一个算法,对于给定的工作费用,计算最佳工作分配方案,使总费用达到最小。 

输入

第一行有 1 个正整数 n (1≤n≤20)。接下来的 n 行,每行 n 个数,表示工作费用。

输出

将计算出的最小总费用输出。

样例输入

3

10 2 3

2 3 4

3 4 5

样例输出

9

#include <iostream>
using namespace std;

int n;
int pay[21][21];   
int Min = 0x3f3f3f3f;   
int sum = 0;  
int book[21];

void dfs(int t)
{
    if (t >= n)
    {
        if (Min > sum)
        {
            Min = sum;
            return;
        }
    }
    for (int i = 0; i < n; i++)
    {
        if (!book[i])
        {
            book[i] = 1;
            sum += pay[t][i];
            if (sum < Min)
                dfs(t + 1);
            book[i] = 0;
            sum -= pay[t][i];
        }
    }
}

int main()
{
    cin >> n;
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < n; j++)
        {
            cin >> pay[i][j];
        }
        book[i] = 0;
    }
    dfs(0);
    cout << Min << endl;
    return 0;
}

题三  特殊的字符串进行编码

在数据加密和数据压缩中常需要对特殊的字符串进行编码.给定的字母表A由26个小写英文字母组成,即A={a,b...z}.该字母表产生的升序字符串是指定字符串中字母从左到右出现的次序与字母在字母表中出现的次序相同,且每个字符最多出现1次.例如,a,b,ab,bc,xyz,等字符串是升序字符串.现在对字母表A产生的所有长度不超过6的升序字符串按照字典序排列并编码如下

        1 2 3 4 ... 26 27 28 ...

        a b c d ...  z  ab  ac ...

对于任意长度不超过6的升序字符串,迅速计算出它在上述字典中的编码

数据输入:第一行是一个正整数k,表示接下来有k行,在接下来的k行中,每行给出一个字符串

数据输出:有k行,每行对应一个字符串的编码

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int cal(int m, int n)
{
	int select = 1, arrange = 1;
	for (int i = m; i > m - n; i--)select *= i;
	for (int j = 1; j <= n; j++)arrange *= j;
	return select / arrange;
}
int ifAlph(char c)
{
	if (c >= 'a' && c <= 'z')return 1;
	return 0;
}
int main()
{
	char arr[10];
	int len = 0;
	int n = 0;
	int out = 0;
	int num[10];
	int temp = 0;
	scanf("%d", &n);
	while(n--)
	{
		len = 0; out = 0;
		for (int i = 0; i < 10; i++)
		{
			arr[i] = '\0';
			num[i] = 0;
		}
		scanf("%s", arr);
		for (int i = 0; i < 10; i++)
		{
			if (ifAlph(arr[i]))
				num[len++] = arr[i] - 96;
				//len++;
		}
		for (int j = 1; j < len; j++)out += cal(26, j);
		temp = 1;
		for (int i = len; i > 0; i--)
		{
			for (int j = temp; j < num[len - i]; j++)
				out += cal(26 - j, i - 1);
			temp = num[len - i] + 1;
		}
		printf("%d\n", out+1);
	}
	return 0;
}

题四 最多约数问题 

正整数的约数是能整除x的正整数,其约数的个数记为div(x),例如,1,2,5,10都是正整数10的约数,且div(10)=4。设a 和b 是两个正整数,a<=b,找出a 和b 之间约数个数最多的数的约数个数。

样例输入:

  1  36

样例输出:

  9

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <math.h>

int countDivisors(int num) {
	int count = 0;
	int sqrtNum = sqrt(num);

	for (int i = 1; i <= sqrtNum; i++) {
		if (num % i == 0) {
			if (i * i == num) {
				count++;
			}
			else {
				count += 2;
			}
		}
	}

	return count;
}

int findNumberWithMostDivisors(int a, int b) {
	int maxDivisors = -1;
	int result = a;

	for (int i = a; i <= b; i++) {
		int divisors = countDivisors(i);
		if (divisors > maxDivisors) {
			maxDivisors = divisors;
			result = i;
		}
	}

	return result;
}

int main() {
	int a, b;
	scanf("%d%d", &a, &b);

	int result = findNumberWithMostDivisors(a, b);
	int maxDivisors = countDivisors(result);

	printf("%d\n", maxDivisors);

	return 0;
}

 题五 众数问题

题目描述

所谓众数,就是对于给定的含有N个元素的多重集合,每个元素在S中出现次数最多的成为该元素的重数, 多重集合S重的重数最大的元素成为众数。例如:S={1,2,2,2,3,5},则多重集S的众数是2,其重数为3 现在你的任务是:对于给定的由m个自然数组成的多重集S,计算出S的众数及其重数。

输入

第一行为n,表示测试数据组数。(n<30) 每组测试的第一行是一个整数m,表示多重集S中元素的个数为m 接下来的一行中给出m(m<100)个不大于10万的自然数 (不会出现不同元素出现的次数相同的情况,如:S={11,11,22,22,33,33})。

输出

每组测试数据输出一行,包含两个数,第一个是众数,第二个是其重数,中间以空格隔开。

样例输入

1

6

1 2 2 2 3 5

样例输出

2 3

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>

int main() {
    int n;
    scanf("%d", &n);

    while (n--) {
        int m;
        scanf("%d", &m);

        int* arr = (int*)malloc(m * sizeof(int));

        for (int i = 0; i < m; i++) {
            scanf("%d", &arr[i]);
        }

        int* count = (int*)calloc(1005, sizeof(int));

        for (int i = 0; i < m; i++) {
            count[arr[i]]++;
        }

        int element, num_max = 0;
        for (int i = 0; i < 1005; i++) {
            if (count[i] > num_max) {
                num_max = count[i];
                element = i;
            }
        }

        printf("%d %d\n", element, num_max);

        free(arr);
        free(count);
    }

    return 0;
}

题六 自然数的乘积最大问题

题目描述

设置n是一个正确的整数。现在要求将n区分为若干不同的自然数之和,并使这些自然数的乘积最大。

输入

由文件input.txt 提供输入数据。文件的第1行是正确的整数n。

输出

程序运行结束时,将计算出的最大乘积输出到文件output.txt中。

样例输入

7

样例输出

12

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int a[1000];

int main() {
    int n;
    scanf("%d", &n);
    if (n < 5) {
        printf("%d\n", n);
        return 0;
    }
    for (int i = 0; i < 1000; i++) {
        a[i] = 0;
    }
    int k = 1, sum = 1;
    a[1] = 2;
    n = n - 2;
    while (n > a[k]) {
        k++;
        a[k] = a[k - 1] + 1;
        n = n - a[k];
    }
    if (n == a[k]) {
        a[k]++;
        n--;
    }
    for (int i = 0; i < n; i++) {
        a[k - i]++;
    }
    for (int i = 1; i <= k; i++) {
        sum *= a[i];
    }
    printf("%d\n", sum);
    return 0;
}

题七 An位数字中删除m个数字问题

题目描述

设A是一个n位的正整数。现给定一正整数m(m<n),从A的n位数字中删除m个数字后,剩余的数字按原顺序排列组成一个新的数字B。现在要求对于给定的n位长度的正整数A和数字m,请设计一个删数方案,使得剩下的数B最小。

算法设计:对于给定的正整数A,计算删除m个数字后的最小数。

输入

第1行是正整数A,第2行是正整数m。

输出

程序运行结束时,将计算出的最大乘积输出。

样例输入

178543

4

样例输出

13

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>

int main() {
    char a[1000];
    int m;
    scanf("%s%d", a, &m);
    int len = strlen(a);
    for (int i = 0; i < m; i++) {
        for (int j = 0; j < len - 1; j++) {
            if (a[j] > a[j + 1]) {
                for (int k = j; k < len - 1; k++) {
                    a[k] = a[k + 1];
                }
                break;
            }
        }
        len--;
    }
    int x = 0;
    while (a[x] == '0' && x <= len - 1) {
        x++;
    }
    if (x == len) {
        printf("0\n");
    }
    else {
        for (; x <= len - 1; x++) {
            printf("%c", a[x]);
        }
        printf("\n");
    }
    return 0;
}

题八 半数集问题

题目描述

给定一个自然数n,由n开始可以依次产生半数集set(n)中的数如下。 (1) nset(n) (2) n的左边加上一个自然数,但该自然数不能超过最近添加的数的一半; (3) 按此规则进行处理,直到不能再添加自然数为止。 例如,set(6)={6,16,26,126,36,136}。半数集set(6)中有6 个元素。 注意半数集是多重集。

输入

对于给定的自然数n,计算半数集set(n)中的元素个数。

输出

程序运行结束时,将计算结果输出。输出只有1 行,给出半数集set(n)中的元素个数。

样例输入

6

样例输出

6

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>

int countElements(int n, int* arr) {
    if (n == 1) {
        return 1;
    }
    if (arr[n] != -1) {
        return arr[n];
    }
    int cnt = 1;
    for (int i = 1; i <= n / 2; i++) {
        cnt += countElements(i, arr);
    }
    arr[n] = cnt;
    return cnt;
}

int main() {
    int n;
    scanf("%d", &n);

    int* arr = (int*)malloc((n + 1) * sizeof(int));

    for (int i = 0; i <= n; i++) {
        arr[i] = -1;
    }

    printf("%d\n", countElements(n, arr));

    free(arr);
    return 0;
}

题九 骑士巡游问题 

这个问题经典了,就是给一个方法让这个无聊的骑士走遍所有的格子,变态一点的会要求起点随机。但是我当时初学的时候很多算法没法解决骑士起点随机的问题,要么就是卡黑屏,以下方法可以解决骑士起点随机,打印骑士遍历全部网格时途径各个网格的顺序(用网格上的代码表示,0为起始)

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int h[9][9];
int dx[10] = { 2, 1, -1, -2, -2, -1, 1, 2 };
int dy[10] = { 1, 2, 2, 1, -1, -2, -2, -1 };
typedef pair<int, int> PII;
int out[10][10] = {
	{0},
	{0, 2, 3, 4, 4, 4, 4, 3, 2},
	{0, 3, 4, 6, 6, 6, 6, 4, 3},
	{0, 4, 6, 8, 8, 8, 8, 6, 4},
	{0, 4, 6, 8, 8, 8, 8, 6, 4},
	{0, 4, 6, 8, 8, 8, 8, 6, 4},
	{0, 4, 6, 8, 8, 8, 8, 6, 4},
	{0, 3, 4, 6, 6, 6, 6, 4, 3},
	{0, 2, 3, 4, 4, 4, 4, 3, 2},
};
int flag = 0;
void dfs(int x, int y, int s)
{
	h[x][y] = s;
	if (flag) return;
	if (s == 64)
	{
		for (int i = 1; i <= 8; ++i)
		{
			for (int j = 1; j <= 8; ++j)
			{
				cout << h[i][j] - 1 << "\t";
			}
			cout << endl;
		}
		flag = 1;
		return;
	}
	vector<int> q;
	for (int k = 0; k < 8; ++k)
	{
		int xx = x + dx[k], yy = y + dy[k];
		if (xx < 1 || yy < 1 || xx>8 || yy>8 || h[xx][yy]) continue;
		q.push_back(xx * 10 + yy);
	}
	sort(q.begin(), q.end(), [](int a, int b) {return out[a / 10][a % 10] < out[b / 10][b % 10]; });
	for (auto qq : q)
	{
		int xx = qq / 10, yy = qq % 10;
		h[xx][yy] = s + 1;
		dfs(xx, yy, s + 1);
		if (flag) return;

	}
	h[x][y] = 0;
}
int main()
{
	int sx, sy;
	cin >> sx >> sy;
	dfs(sx, sy, 1);
	return 0;
}

  • 15
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值