牛客.哈尔滨工程大学第十四届程序设计竞赛 (F.I.L)

本文探讨了两个算法问题:一是寻找迷宫最短路径的最小花费,涉及动态规划和状态枚举;二是根据得分分配糖果的最优策略,通过前后扫描记录相连的小于当前值的数量来解决。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

F 小帆帆走迷宫

链接:https://ac.nowcoder.com/acm/contest/642/F
来源:牛客网

题目描述

小帆帆被困在一个 NxN 的方格矩阵迷宫,每个格子中都有一个整数 A[i][j]。小帆帆从迷宫起点(左上角)格子 A[1][1]开始走,每一步可以向右或向下移动,目标是移动到迷宫的出口右下角 A[N][N]。 小帆帆需要支付的费用包括路径中经过的所有格子中的整数之和,以及改变移动方向需要支付的费用。 小帆帆第一次改变方向的费用是 1,第二次的费用是 2,第三次的费用是 4,…… 第 K 次的费用是2?−1。 请你帮小帆帆算出要离开迷宫的最小花费。
输入描述:
第一行一个整数T,代表测试数据组数。每一组第一行一个整数N。 (1 ≤ N ≤ 100)以下N行每行N个整数,代表矩阵A。 (1 ≤ A[i][j] ≤ 100)
输出描述:
从起点到终点路径的最小花费。

示例1
输入

2
1
10
3
1 3 5
1 1 2
5 1 1

输出

10
9

枚举每个格点对应可由哪些格点得来,然后枚举上个结点的状态,由于转弯次数过多所以对于过多的情况直接不用枚举

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const int maxn = 110;
int mp[maxn][maxn];//地图
int f[maxn][maxn][30][2];
//记录到达某个点的代价,转弯的次数,当前的方向
//0表示向下走,1表示向右走
int w[25];

int main(){
	int T;
	cin >> T;
	w[0] = 1;
	for (int i = 1; i <= 25; i++)
		w[i] = w[i - 1] * 2;
	while (T--){
		int n;
		cin >> n;
		memset(f, 0x3f, sizeof f);
		for (int i = 1; i <= n;i++)
		for (int j = 1; j <= n; j++)
			cin >> mp[i][j];
		f[1][1][0][0] = mp[1][1];
		f[1][1][0][1] = mp[1][1];
		for (int i = 1; i <= n;i++)
		for (int j = 1; j <= n; j++){
			for (int k = 0; k <= 20; k++){
				f[i][j][k][0] = min(f[i][j][k][0], f[i][j-1][k][0] + mp[i][j]);//上个格子没转弯向下走
				f[i][j][k + 1][0] = min(f[i][j][k + 1][0], f[i][j - 1][k][1] + w[k] + mp[i][j]);
                //上面个格子转弯后到达这个点
			}
			for (int k = 0; k <= 20; k++){
				f[i][j][k][1] = min(f[i][j][k][1], f[i - 1][j][k][1] + mp[i][j]);
				f[i][j][k + 1][1] = min(f[i][j][k + 1][1], f[i - 1][j][k][0] + w[k] + mp[i][j]);
                //同理向右的情况
			}
		}
		int  sum = 0x3f3f3f3f;
		for (int i = 0; i <= 20; i++)//枚举转弯次数为几时的总代价,取最小
			sum = min({ sum, f[n][n][i][0], f[n][n][i][1] });
		cout << sum << endl;
	}
	return 0;
}

I杨主席发糖

题目描述

19 年校赛的结果已经出炉,为了对大家的积极参与表示感谢,杨主席准备拿出他的工资给大家发福利。 所有参赛选手排成一排,每位同学都对于有一个量化的得分(当然,得分越高越好)。杨主席已事先知道选手们的排队顺序以及个人的得分,他将一个一个给大家发糖。 杨主席是个有原则的男人,但他原则也是有限的。所谓有原则是指:分数高的一定比分数低的能拿到更多的糖果且分数 一样的拿到的糖果一样多;所谓原则是有限的又是指:刚刚说的原则仅对排队中相邻的两人生效,且允许有人拿不到糖果(杨主席是个穷逼)。 杨主席并不想多花一分钱买额外的糖,于是他想知道在各种情况下最少需要购入几块糖。
输入描述:
首先是一个整数T,表示数据输入的组数,T<=20。对于每组数据,有两行:(1)一个整数n,表示队列中的人数,0 < n <= 1000。(2)n个整数a[0] … a[n-1],表示按队列顺序每个人的得分,注意可能会有同分的情况,保证a[i]是int类型的。
输出描述:
对应T行,每行即对应每组输入的最少购入糖数结果。

示例1

输入

2
6
12 13 14 5 10 13
6
2 2 2 2 2 2

输出

6
0

对于当前位置用数组分别前后扫一遍记录相连的有几个比自己小的,最后用数组遍历取每个位置的最大值求和

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1010;
int f[maxn], g[maxn], a[maxn];
int main(){
	int T;
	cin >> T;
	while (T--){
		int n;
		cin >> n;
		memset(f, 0, sizeof f);
		memset(g, 0, sizeof g);
		for (int i = 1; i <= n; i++)
			cin >> a[i];
		for (int i = 2; i <= n; i++){
			if (a[i] == a[i - 1])
				f[i] = f[i - 1];
			else if (a[i] > a[i - 1])
				f[i] = f[i - 1] + 1;
			//else f[i] = 0;
		}
		for (int i = n - 1; i >= 1; i--){
			if (a[i] > a[i + 1])
				g[i] = g[i + 1] + 1;
			else if (a[i] == a[i + 1])
				g[i] = g[i + 1];
			//else g[i] = 0;
		}
		int sum = 0;
		for (int i = 1; i <= n; i++){
			sum += max(g[i], f[i]);
		}
		cout << sum << endl;
	}
	return 0;
}

L.实际问题

题目描述

本题的出题人在实习(摸鱼)中遇到了一个非常有意思的问题,这个问题的核心即需要枚举“组合”。 组合即数学上的“C”的概念,我们知道 C(x,y)=y!/x!/(y-x)! 如果我们想枚举 C(3,4),我们有: 1 2 3 1 2 4 1 3 4 2 3 4 这道题就是这个意思,当然为了简化输出,我们只要你给出按顺序的第 k 个组合即可,如 C(3,4)的第 3 个是:1 3 4 注意组合内部是无序的,但是我们希望得到一个递增顺序的结果,即结果是 1 3 4,而非诸如 1 4 3 之流。
输入描述:
首先是一个整数T,表示数据输入的组数,T<=10。对于每组数据,一行三个数n,m,k:欲求C(m,n)的第k个组合。保证n<=100000,m<=n,k<=1000000,k<=C(m,n)。
输出描述:
对应T行,每行即结果。

输入

2
6 3 10
100 5 1000

输出

1 5 6
1 2 3 14 99

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const int maxn = 1e5 + 5;
ll num[maxn];
int n, m, k, flag;
int cnt;
void dfs(int x, int y){//y为这组的第几个
	if (flag) return;
	if (y == m+1){
		cnt++;
		if (cnt == k){
			for (int i = 1; i < m; i++)
				cout << num[i] << " ";
			cout << num[m] << endl;
			flag = 1;
		}
		return;
	}
	for (int i = x; i <= n - m + y; i++){//选下一个或者不选
		num[y] = i;
		dfs(i + 1, y + 1);//选过不能重复选  i+1
		if (flag) return;
	}
}
int main(){
	int T;
	cin >> T;
	while(T--)
	{
		cin >> n >> m >> k;
		memset(num, 0, sizeof num);
		flag = 0;
		cnt = 0;
		dfs(1, 1);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值