个人场第十场题解

个人场第十场题解

花札洗牌

题目描述
洗牌的方法有很多种。日本纸牌游戏“hanafuda”的洗牌就是这样一个例子。下面是如何执行Hanafuda洗牌。

有一副n张牌。从卡组顶部的第p张卡开始,c卡被拉出并放在卡组顶部,如图1所示。此操作称为切割操作,然后重复进行。

编写一个程序,模拟花花公子洗牌,并回答哪张牌将最终放在牌组顶部。

在这里插入图片描述
输入格式
输入包含多组数据。

对于每组数据首先输入一行两个用空格分开的整数n和r,分别是组中的卡数和剪切操作数1≤n≤50,1≤r≤50。
接下来输入r行,每行代表一次切割操作。这些切割操作按列出的顺序执行。每行包含两个正整数p和c(p+c<=n+1)。从卡组顶部的第p张卡开始,c张卡应抽出并放在顶部。

输入以两个用空格分开的0结尾

输出格式
对于每组数据,输出最后在牌堆顶部的牌的编号,牌最初从底部到顶部编号为1-n

题目大意:

有一堆卡片,编号从1.。。n,从第p个卡片开始向后取出c张卡片放到底部。这里的顶部是第n个后边。同时注意的是在将卡片放到顶部的时候,之前在前边的卡片后到后边。

解题思路:

我们用数组来模拟这个过程,我们从前往后存n~1,这个时候下标为1的地方就是顶部。同时用一个额外的数组来存操作后的数据。模拟的过程是将c张卡片放到前面c个位置,然后才将之后的数据填入。

代码:

#include <stdio.h>
#include <cstring>

int game[100];
int n, r;

int main() {
	while (~scanf("%d%d", &n, &r)) {
		if (!n && !r)
			break;
		for (int i = 0; i <= n; ++i)
			game[i] = n - i + 1;
		int barcup[200];
		memcpy(barcup, game, sizeof game);
		// 以 10 9 8 7 6 5 4 3 2 1 为例
		while (r--) {
			int p, c;
			scanf("%d%d", &p, &c);
			// p = 8,c = 3;
			// 将第8个开始的3张放到顶部,即 4,3,2
			for (int i = p + c - 1, j = c; i >= p; --i, --j)
				barcup[j] = game[i];
			// 4 3 2 7 6 5 4 3 2 1
			// 之后将 第1位到第 p-1 为的元素放到后边
			for (int i = c + 1, j = 1; j <=  p - 1; i++, ++j)
				barcup[i] = game[j];
			// 4 3 2 10 9 8 7 6 5 1
			memcpy(game, barcup, sizeof game);
		}
		printf("%d\n", game[1]);

	}
	return 0;
}

点与圆

在笛卡尔坐标系中已知N个点。你有一个半径为1的圆,并在笛卡尔坐标系中移动它,以便尽可能多地包围这些点。找出有多少点可以同时被包围在最大。当点在圆内或圆上时,它被认为是被圆包围的。
在这里插入图片描述
输入
输入由一系列数据集组成,后面跟着一行,只包含一个字符’0’,这表示输入的结束。 每个数据集都以包含整数N的行开始,N表示数据集中的点数。 后面N行描述点的坐标。 每一行都有两个小数X和Y,分别描述点的X和Y坐标。 小数点后保留五位数字。

你可以假设1 <= N <= 300, 0.0 <= X <= 10.0, 0.0 <= Y <= 10.0。 没有两个点之间距离小于0.0001。 数据集中没有两个点的距离近似为2.0。 更准确地说,对于数据集中的任意两点,两者之间的距离d永远不满足1.9999 <= d <= 2.0001。 最后,一个数据集中没有三个点同时非常接近一个半径为1的圆。 更准确地说,让P1, P2, P3是数据集中的任意三个点,d1, d2, d3分别是笛卡尔坐标系中任意点到这三个点的距离。 然后它永远不会同时持有0.9999 <= di <= 1.0001 (i = 1,2,3)。

输出
对于每个数据集,打印单行,其中包含数据集中可以同时被半径为1的圆包围的最大点数。 不应该打印其他字符,包括前导和尾随空格。

解题思路是,将问题转化为特定形式的答案。画个图我们假设有一个圆包含了一推点在里边,我们在可以一直包含所有点的情况下移动圆,我们首先呢圆与其中一个点相交,这个时候是满足的,当与两个点相交的时候,如果还移动圆就无法包含所有的点了。因此极限就是两个点。
因此我们可以枚举两个不同点的组合,通过这两个点算出过这两个点的圆的圆心,之后因为是单位元,遍历所有点如果与圆心的距离在圆内就+1,取这个结果的最大值。

注意事项:

  • 一个点的情况,所以我们初始化为1即可。
  • 因为题目描述如果两点距离大于2就不计算。

代码:

#include <stdio.h>
#include <math.h>
#include <algorithm>

using namespace std;
const double eps = 1e-8;
struct point {
	double x, y;
} p[3010];
int n;


double dist(point a, point b) {
	return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}

// 已知圆上两点,求圆心坐标
void getmid(point p1, point p2, point &center) {
	point mid;
	mid.x = (p1.x + p2.x) / 2.0;
	mid.y = (p1.y + p2.y) / 2.0;
	double angle = atan2(p1.x - p2.x, p2.y - p1.y);
	double dcm = sqrt(1 - dist(p1, mid) * dist(p1, mid));
	center.x = mid.x + dcm * cos(angle);
	center.y = mid.y + dcm * sin(angle);
}

int main() {
	while (~scanf("%d", &n)) {
		if (!n)
			break;
		for (int i = 1; i <= n; ++i)
			scanf("%lf%lf", &p[i].x, &p[i].y);
		int ans = 1, cnt = 0;
		for (int i = 1; i < n; ++i)
			for (int j = i + 1; j <= n; ++j) {
				if (dist(p[i], p[j]) > 2.0)
					continue;
				point center;
				getmid(p[i], p[j], center);
				int cnt = 0;
				for (int k = 1; k <= n; ++k) {
					if (dist(p[k], center) < 1.0 + eps)
						cnt++;
				}
				ans = max(ans, cnt);
			}
		printf("%d\n", ans);
	}
	return 0;
}

单位分数划分

输入格式
分子为1,分母为正整数的分数称为单位分数。正有理数p/q作为有限多个单位分数之和的表示称为p/q到单位分数的划分。例如,1/2+1/6是将2/3划分为单位分数的一种方式。加法顺序的差异被忽略。例如,我们不区分1/6+1/2和1/2+1/6。

对于给定的四个正整数p、q、a和n,将p/q的分区数计算为满足以下两个条件的单位分数。

划分为最多n个单位分数的总和。

划分中单位分数分母的乘积小于或等于a
例如,如果(p,q,a,n)=(2,3,120,3),能够找到如下四种划分
在这里插入图片描述
输入格式
输入不超过1000组数据

对于每组数据,输入p q a n,相邻两个数以空格分开,p,q≤800,a≤12000,n≤7

输入以包含四个用空格分开的0结束

输出格式
对于每组数据,输出一个整数,表示划分方案数

题意:

给了一个分数p/q,求有多少种单位分数之和等于p/q,且满足所有单位分数的分母之积小于等于a,个数小于n。

思路:

分母从1开始枚举,每次记录一下枚举到哪个数了,如果满足分母之ji积小于等于a,且个数不超过n,从此数继续让下搜,因为单位分数之间不分先后顺序,所以这样就涵盖了所有情况。

代码:

#include <stdio.h>
#include <cstring>

int p, q, a, n;
int ans;
void dfs(int p, int q, int mul, int x, int num) {
	if (p == 0 && mul <= a) {
		ans ++;
		return ;
	}
	// 剪枝
	// 分别为 数量达到上限,mul * x 是等会分母要乘的最小
	//的的值这都大于 a 了就不用玩了。
	// p*x>q*num 就是说因为这一条路径往后分母乘的都>= x
	if (num == 0 || mul * x > a || p * x > q * num)
		return ;
	for (int i = x; i <= a ; ++i) {
		if (mul * i <= a) {
			int dp = p * i - q; // 不能直接改变p呀不然这一曾就不是以p为根了
			if (p >= 0)
				dfs(dp, q * i, mul * i, i, num - 1);
		}
	}
}


int main() {
	while (~scanf("%d%d%d%d", &p, &q, &a, &n)) {
		if (!p && !q && !n && !a)
			break;
		ans = 0;
		dfs(p, q, 1, 1, n);
		printf("%d\n", ans);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

落春只在无意间

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值