最大三角形

任务描述

给出平面内n个点,求以这n个点中的三个点为顶点的三角形的最大面积。三点共线时认为三角形的面积为0。 范围: 3 <= n <= 100 所有的点的坐标为整数,且其绝对值不超过127

相关知识

假设三角形三个顶点为 $(x_1,y_1)$ , $(x_2,y_2)$, $(x_3,y_3)$, 那么面积为 $\frac{1}{2}|(x_2-x_1)(y_3-y_1)-(x_3-x_1)(y_2-y_1)|$ 。(证明可用向量积得到)

编程要求

实现 getMaxTriangle() 函数。

测试说明

平台会对你编写的代码进行测试:

测试输入:

5
0 0
1 1
1 -1
-1 -1
-1 1

预期输出: max area = 2

参考代码

#include <iostream>
#include <cassert>
#include <cmath>
using namespace std;

//n points are (x[0],y[0]), (x[1],y[1]), ... ,(x[n-1],y[n-1])
double getMaxTriangle(int n, int x[], int y[])
{
    // write your code here
    int ans = 0;
    for (int i=0;i<n;i++) {
        for (int j=i+1;j<n;j++) {
            for (int k=j+1;k<n;k++) {
                int tmp = abs((x[j]-x[i])*(y[k]-y[i])-(x[k]-x[i])*(y[j]-y[i]));
                if (tmp > ans) ans = tmp;
            }
        }
    }
    return ans/2.0;
}

int main(void)
{
    int n, x[101], y[101];
    cin >> n;
    assert(3 <= n && n <= 100);
    for (int i=0;i<n;i++)
        cin >> x[i] >> y[i];
    cout << "max area = " << getMaxTriangle(n, x, y) << endl;
    return 0;
}

孔融分梨

任务描述

孔融没有兄弟姐妹,到了周末,就找堂兄孔明、堂姐孔茹、堂弟孔伟等7个堂兄妹来到家里玩。孔融妈妈买了8个梨给孩子们吃,结果小黄狗桐桐淘气叼走了一个,大花猫鑫鑫偷偷藏了一个。孔融抢过剩下的6个梨,妈妈止住他,说他要和大家平分吃。孔融不高兴,说8个人怎么分6个梨?妈妈说可以用分数解决这个问题。孔融学过分数,说把每个梨切8个相等的块,每个人拿6块就行了。妈妈说不用切那么多块,每个梨切4个相等的块,每个人拿3块正好。孔融糊涂了。孔明说,我来教你。于是孔明给孔融讲起了分数的化简。

分数化简是指,对于正整数m,n,q,p,如果分数 m/n = p/q 且p与q互质,则称p/q是m/n的最简形式,比如12/20的最简形式是3/5;100/8的最简形式是25/2。

请编程帮助孔融将任意一个分数化简成最简形式。先从键盘输入两个整数m和n(1<=m,n<=10000) ,其中m表示分子,n表示分母。然后输出分数化简后的最简形式。

函数原型:int Gcd(int a, int b); 函数功能:计算a和b的最大公约数

编程要求

根据提示,在右侧编辑器补充代码,编写函数使之符合要求。

测试说明

平台会对你编写的代码进行测试:

测试1: 测试输入: 8 14 测试输出: 4/7

测试2: 测试输入: 210 35 测试输出: 6/1

参考代码

#include <iostream>
#include <cassert>
using namespace std;

int Gcd(int m, int n)
{
    // write your code here
    for (int i=m;i>=1;i--)
        if (n%i==0 && m%i==0) return i;

    assert(0);
}

int main()
{
    int m, n, ans;
    cin >> m >> n;

    // write your code here
    int g = Gcd(m, n);
    cout << m/g << '/' << n/g << endl;
    return 0;
}

递归法计算游戏人员的年龄

任务描述

本关任务:编写一个使用递归法计算游戏人员年龄的小程序。

有n个人围坐在一起,问第n个人多大年纪,他说比第n-1个人大2岁;问第n-1个人,他说比第n-2个人大2岁,…..,问第3个人,他说比第2个人大2岁;问第2个人,他说比第1个人大2岁。第1个人说自己10岁,问第n个人多大年纪。(范围 n <= 50)

编程要求

根据提示,在右侧编辑器补充代码,使用递归法计算游戏人员年龄并输出。

测试说明

平台会对你编写的代码进行测试:

测试输入:5 预期输出: 18

测试输入:10; 预期输出: 28

参考代码

#include<iostream>

using namespace std;

int f(int n)
{
    // write your code here
    // make sure that the implementation is by **recursion**
    return n==1?10:(f(n-1)+2);
}
int main()
{
    // write your code here
    int n; cin >> n;
    cout << f(n) << endl;
    return 0;
}

数组对调

任务描述

设计一个程序,让用户输入两个数组,然后将它们中下标相同的非空元素对调,再输出这两个新的数组。用户输入的两个数组元素数量都小于20个。

限制条件

  1. 除了<iostream>之外不可包含其它库;
  2. 只能使用已定义的三个int变量、四个int指针,不能定义别的变量、数组或其它对象 (包括 for (int i;…;…) 也算定义了新的变量 i)。

输入输出示例

第一行 n m 第二行 $a_0$ $a_1$ … $a_{n-1}$ (第一个数组) 第三行 $b_0$ $b_1$ … $b_{m-1}$ (第二个数组)

样例输入:

5 6
1 2 3 4 5
6 5 4 3 2 1

样例输出:

6 5 4 3 2
1 2 3 4 5 1

参考代码

#include <iostream>
using namespace std;

int main()
{
    int n, m;
    cin >> n >> m;
    int * a = new int [n];
    int * b = new int [m];
    int tmp, * p1, * p2;

    /*Start your code here*/
    for (tmp=0; tmp<n; tmp++) cin >> a[tmp];
    for (tmp=0; tmp<m; tmp++) cin >> b[tmp];
    for (p1=a, p2=b; p1-a<n && p2-b<m; p1++, p2++)
        tmp = *p1, *p1 = *p2, *p2 = tmp;
    for (tmp=0; tmp<n; tmp++) cout << a[tmp] << " \n"[tmp==n-1];
    for (tmp=0; tmp<m; tmp++) cout << b[tmp] << " \n"[tmp==m-1];

    /*end your code*/

    return 0;
}

加密字符

任务描述

编写程序,将输入的一个字符串进行加密和解密。加密时,每个字符依次反复加上[8,7,3,4,9,6,2]中的数字,如果范围超过ASCII码的032(空格)- 122('z'),则进行模运算。解密与加密的顺序相反。请编写加密和解密的过程,并打印出结果。注:要加密的字符串长度不会超过30。

说明

  1. 字符的范围。无论时输入的字符还是加密后的字符,字符的ASCII值都需要控制在032(空格)- 122(’z’)之间,即[32,122]
  2. 加密。把待加密字符串7个字符分为一组,每组对应位依次加上[8,7,3,4,9,6,2] 中的数字。如果执行加法后得到的数值 N 超过[32,122],则需要调整。加密公式为32+(N-32)%(122-32+1),当N>122时在本题目中加密公式可简化为 N-91。
  3. 解密。解密的过程与加密的过程顺序正好相反。如果执行减法后得到的数值不在[32,122],则需要调整。解密公式为32+[N-32+(122-32+1)]%(122-32+1);当N<32时在本题目中可简化为 N+91.

输入输出示例

input: wherewithal

output: $ohvn"k!odp wherewithal

待加密字符 加法运算 加密后字符的ASCII码 加密后字符 减法运算 解密后字符的ASCII码 解密后字符
w 119+8=127 127-91=36 $ 36-8=28 28+91=119 w
h 104+7=111 111 o 111-7=104 104 h
e 101+3=104 104 h 104-3=101 101 e
r 114+4=118 118 v 118-4=114 114 r
e 101+9=110 110 n 110-9=101 101 e
w 119+6=125 125-91=34 34-6=28 28+91=119 w
i 105+2=107 107 k 107-2=105 105 i
t 116+8=124 124-91=33 ! 33-8=25 25+91=116 t
h 104+7=111 111 o 111-7=104 104 h
a 97+3=100 100 d 100-3=97 97 a
l 108+4=112 112 p 112-4=108 108 l

参考代码

#include <iostream>
using namespace std;

void encrypt(char * s)
{
    // write your code here
    int t[7] = {8,7,3,4,9,6,2};
    for (int i=0;s[i];i++) {
        s[i] += t[i%7];
        if (s[i] > 122) s[i] -= 91;
    }
}
void decrypt(char * s)
{
    // write your code here
    int t[7] = {8,7,3,4,9,6,2};
    for (int i=0;s[i];i++) {
        s[i] -= t[i%7];
        if (s[i] < 32) s[i] += 91;
    }
}
// do not change main function
int main(void)
{
    char s[35];
    cin.getline(s, 31);
    encrypt(s);
    cout << s << endl;
    decrypt(s);
    cout << s << endl;

    return 0;
}

机器人的路线数量(附加题,选做)

任务描述

如图所示,有一个m行n列的网格。起点坐标为(m,n),终点坐标为(1,1)。有一个机器人从起点出发,每次移动只能 移动到当前方格的下方方格或右方方格。请设计函数计算,给定方格的行m列n,机器人可以有几种路线到达终点。

, 例如,当m = n = 2时,共有两种可能的路线。 ,

编程要求

程序输入两个整数m,n,输出 路线数量。

测试说明

平台会对你编写的代码进行测试:

测试输入: 1 1 预期输出: 1

测试输入: 2 2 预期输出: 2

测试输入: 5 5 预期输出: 70

提示:

可以将原问题转化为两个子问题: 如果在(m, n)向右走,则变成了求解输入为(m,n-1)的子问题,即列数变少1单位。如果向下走,则变成了求解输入为(m-1,n)的子问题,即行数变少1单位。而原问题的答案就是两个子问题答案之和。

上述将原问题分解为子问题的递归思路尽管正确,但是会造成许多重复计算。例如 $f(5,5) = f(5,4) + f(4,5);f(4,5) = f(3,5) + f(4,4);f(5,4) = f(4,4) + f(5,3)$ 可以注意到f(4,4)被重复计算。实际上当mn很大时,重复计算现象会很严重。而事实上对于一个(i,j),f(i,j)只需要计算一次。我们可以用一个二维数组$dp[][]$记录每个计算结果,即$dp[i][j] = f(i,j)$.首先我们将$dp[i][1],dp[1][j]$都初始化为1,对于其他的$i,j$,按照一定的顺序计算$dp[i][j] = dp[i-1][j] + dp[i][j-1]$。

注意:此题目测试集可以保证使用递归函数不会超时或栈溢出。

不要说我们一无所有,我们要做全天下的主人 ——《国际歌》

参考代码

#include <iostream>

using namespace std;

int cal_routines(int m ,int n){
    // write your code here
    return (m==1||n==1)?1:(cal_routines(m,n-1) + cal_routines(m-1,n));
}
// NEVER EDIT THE MAIN FUNCTION
int main()
{
    int m, n;
    cin >> m >> n;
    cout << cal_routines(m, n) << endl;
}

二进制小数(附加题,选做)

任务描述

给定一个十进制下的非负小数(包括整数),输出它在二进制下的表示,如果有循环节,在循环节两边加上小括号表示。如 $3.6{(10)} = 11.(1001){(2)}$

编程要求

根据提示,在右侧编辑器补充代码。 不限制具体实现形式,请综合的使用所学知识。

测试说明

输入

一个非负小数n(包括整数) (0<=n<65536),如果存在小数点,n的小数点后不超过2位。 保证n无前导0,但小数点末尾可能有0。

输出

输出n的二进制表示。 请保证输出的整数部分无前导0。 小数部分如果是0,不输出小数点及小数部分。 小数部分如果非0且是有限的,不输出末尾的0。 小数部分如果非0且是无限的,则必有循环节,循环节用小括号括起来。且所有表示中输出长度最短的表示。

样例输入1:10.00 样例输出1:1010 样例解释1:小数部分为0,直接输出整数部分。

样例输入2:5.25 样例输出2:101.01 样例解释2:小数部分非0且有限。

样例输入3:0.1 样例输出3:0.0(0011) 样例解释3:小数部分非0且无限,这时候可以表示为0.0(0011),0.00(0110),0.0(00110011)等,其中长度最短的是0.0(0011)

参考代码

#include <iostream>
#include <cstdlib>
#include <cstring>

using namespace std;

const double eps = 1e-8;
int vis[100];
int s[111], tot;
int main(void) {
    double n;
    cin >> n;
    int x = (int)(n+eps);
    int y = (n - (int)x)*100+eps;
    int ff=0;
    for (int i=20;i>=0;i--) if (ff || (x>>i)&1) {
        cout << ((x>>i)&1);
        ff=1;
    }
    if (ff == 0) cout << 0 << '\n';
    if (y == 0) return 0;
    cout << '.';
    memset(vis,-1,sizeof(vis));
    int f=0, v;
    vis[y] = 0;
    for (int i=0;i<1000;i++) {
        y = 2*y;
        if (y >= 100) s[tot++]=1, y -= 100;
        else s[tot++]=0;
        if (vis[y] >= 0) {f=1; v=vis[y]; break;}
        vis[y] = tot;
        if (y == 0) break;

    }
    if (f == 0) {
        for (int i=0;i<tot;i++) cout << s[i];
        cout << '\n';
    } else {
        for (int i=0;i<v;i++) cout << s[i];
        cout << "(";
        for (int i=v;i<tot;i++) cout << s[i];
        cout << ")\n";
    }
    return 0;
}

游戏(附加题,选做)

任务描述

X和Y正在玩一个游戏。 这个游戏初始时有n个小于10的非负整数排成一排,X和Y轮流进行自己的回合,由X先开始。 在自己的回合里, 1)玩家可以选择一个非负整数$a_i$,并把这个数字变成比$a_i$小的非负整数; 2)也可以选择一个0,并且把这个0和它后面的数都去掉。

如果某个玩家在自己回合内去掉了所有数字,那么他将获得胜利。 X和Y都非常聪明,所以他们都会使用最佳的策略。请问X和Y谁会是最终的赢家?

范围:$1\leq n\leq 5, 0 \leq a_i \leq 3$.

提示

使用递归来计算某个局面下X或Y的最佳决策。 假设当前处于某个局面,最佳决策是: 1)如果这个局面中某个决策演化到下一个局面,下一个局面对于先行者是必败的状态,那么当前的决策者必然选择这一决策,从而获胜。 2)如果这个局面下的所有决策演化到下一个局面,下一个局面对于先行者都是必胜的状态,那么决策者无论怎么选择,都是必败。 3)对于这个游戏,“剩下0个数”的局面是一个对于先行者的必败的状态。

使用伪代码表示这一过程就是:

bool f(局面 A)
{
	for each (从局面 A 可到达的) 局面 B {
    	// 如果局面 B 是必败局面
        // 则玩家就会选择这个 局面 B(使对手必败)
        // 从而在局面 A 是必胜局面
    	if (f(B) == false)
        	return true;
    }
    // 如果不存在局面 A 可到达的局面 或
    // 局面 A 可到达的都是必胜局面
    // 则局面 A 是必败局面
    return false;
}

可以使用一个数组以及一个整数(表示该数组的长度)来表示一个局面,可以遍历数组来找到局面 A 可到达的所有局面 B。

最后,如果初始局面是必胜局面,则X赢,否则Y赢。

编程要求

根据提示,在右侧编辑器补充代码。

测试说明

输入

第一行一个正整数n $(1\leq n\leq 5)$,第二行n个非负整数$a_1,a_2,…,a_n (0\leq a_i\leq 3)$, 两个相邻的数以1个空格相间。

输出

输出赢家,‘X’ 或 ‘Y’。

样例输入:

4
0 3 2 1

样例输出:X 样例解释:X在第一回合选择第1个0,局面变成空。因此X必胜。

样例输入:

3
1 3 2

样例输出:Y

参考代码

#include <iostream>
#include <cstring>
using namespace std;
int f[7][1000000];
bool dfs(int s, int n) {
    if (f[n][s]!=-1) return (bool)f[n][s];
    if (n == 0) return false;
    bool ok=false;
    int o = 1;
    for (int i=0;i<n;i++) {
        int k = (s / o % 10);
        if (k > 0) {
            for (int j=0;j<k;j++) {
                if (!dfs(s-k*o+j*o, n)) {ok = true; break;}
            }
        } else {
            if (!dfs(s/(10*o), n-i-1)) ok = true;
        }
        o *= 10;
    }
    f[n][s]=ok;
    return ok;
}

int main(void) {
    int n, t[10];
    cin >> n;
    for (int i=0;i<n;i++) cin >> t[i];
    memset(f, -1, sizeof(f));

    int s=0;
    for (int i=0;i<n;i++) s=(s*10)+t[i];
    bool f = dfs(s, n);
    cout << (f?"X\n":"Y\n");

    return 0;
}