upc 个人训练赛第六场:校门内的树(二分)

问题 F: 校门内的树

题目描述
FZYZ 大门的左侧有一排 n 棵树木。它们按照距离的远近排列,第 1 棵树的高度为 a1 米,第 2 棵树木的高度为 a2 米,第 3 棵树木的高度为 a3 米,……,第 n 棵树木的高度为 an米。

为了给同学们以积极向上的感觉,一些同学自发地决定对树木进行修剪,使得树木呈现上升的趋势。具体地说,他们希望对树木进行修剪和整理,使得修剪之后的树木高度 b1,b2,b3,…,bn 米且满足 b1

他们不仅可以对较高的枝条进行修剪使其高度减小,还可以通过枝条的加固使得树木的高度增加,而且可以使树木的高度减小和增加任意的高度,但一定得是整数(单位为米),而且最后树的高度必须大于零。然而,树木的整理只能在课间进行,因此他们没有太多的时间。对于一棵树,将其修剪使其高度减少 x 米需要花费 x 分钟的时间,将其整理加固使其高度增加 x 米也需要花费 x 分钟的时间。

参加这次活动的同学超过 n 个,因此所有树木可以同时得到修剪或整理。请你帮他们求出,最少要花费多少的时间可以修剪使得树木递增。注意:花费的总时间取决于最后完成修剪或整理的同学。
输入
第一行包含 1 个整数 n,表示树木的个数。
第二行包含 n 个整数 a1,a2,a3,…,an,表示第 1 棵树的高度、第 2 棵树的高度、第 3 棵树的高度、……、第 n 棵树的高度。
输出
一行包含1个整数,表示能修剪使得树木具有“向上的趋势”的最短时间。
样例输入 Copy
【样例 1 】
3
9 5 11
【样例 2 】
2
5 8
【样例 3 】
5
1 1 1 1 1
【样例 4 】
5
548 47 58 250 2012
样例输出 Copy
【样例 1 】
3
【样例 2 】
0
【样例 3 】
4
【样例 4 】
251
提示
对于 40% 的数据,n≤2;
对于 60% 的数据,n≤15;
对于 80% 的数据,ai≤3000;
对于 100% 的数据,n≤50,ai≤10^9。

思路:
很明显的最大值最小,用二分解决
这里还有一点点贪心的思想,我们希望第一棵树尽量的矮,这样的话后面的树可以修剪的就相对要少,花费时间就少,于是我们判断第一棵树高度 - x是否大于1,如果小就取第一棵树高度为1。剩下的从第二棵树开始循环,每次判断当前树的高度 - x是否大于前一棵树的高度+1,我们希望尽量要小,所以如果大于就直接取上一棵树高度+1。每次二分去寻找这个答案即可

int a[60],b[60];
int n,ans,maxx;
int check(int x)
{
     
	if(x < 0)	return 0;
	b[1] = a[1]-x>1?a[1]-x:1;
	//我们希望第一棵树尽量小
	for(int i=2;i<=n;i++)
	{
     
		if(a[i]+x <= b[i-1])	return 0;
		//没法加高 
		b[i] = a[i]-x<b[i-1]+1?b[i-1]+1:a[i]-x;
		//如果减去这个相同的数比前一个高度+1矮,就只能取前一个高度+1,保证是整数 
	}
	return 1;
}
int main()
{
     
	n = read();
	for(int i=1;i<=n;i++)
	{
     
		cin >> a[i];
		maxx = max(maxx,a[i]);//找最大高度 
	}
	int r = maxx+10,l = -1;
	//找到中间高度二分答案 
	while(l+1 < r)
	{
     
		int mid = (l+r)/2;
		if(check(mid))	r = mid;
		//说明可以花费更小 
		else l = mid;
	}
	cout << r << endl;
	return 0;
}

问题 A: 报数游戏

题目描述
在一次班队活动上,班主任张老师设计了一个“报数游戏”的活动。游戏规则是这样的:每次游戏有甲、乙二位同学参加,甲按 1—a 的顺序循环报数,乙按 1—b 的顺序循环报数。两人同时开始,并以同样的速度报数,当两人都报了 n 个数时,统计出两人同时报相同数的次数,先算对者获胜。现在老师请你来做裁判,算出每次游戏的正确答案。
输入
共二行。第一行仅有一个整数 n(n<=100),第二行有二个整数a、b,中间用空格间隔(2<=a、b<=10)。
输出
只有一行,有一个整数,表示两人同时报相同数的次数。
样例输入 Copy
10
2 3
样例输出 Copy
4

思路:
这个题一层循环即可,在一层大循环中定义两个变量i 和 j控制报数,相等就ans++,一旦满足i == a,就让i = 0,同理j也是这样处理

int n,a,b,ans,cnt;
int i,j;
int x[105];
int main()
{
     
    cin >> n;
    cin >> a >> b;
    while(n--)
    {
     
        i++,j++;
        if(i == j)  ans++;
        if(i == a)  i = 0;
        if(j == b)  j = 0;
    }
    cout << ans << endl;
    return 0;
}

问题 B: 趣味填空

题目描述
小华的寒假作业上,有这样一个趣味填空题:
给出用等号连接的两个整数,如“1234=492”。当然,现在这个等式是不成立的。
请你在等号左边整数中的某个位置尝试插入一个乘号,看有没有可能让等式成立。以上
面的式子为例,如果写成 123*4=492,这样就正确了。
现在请你编写一个程序来解决它。
输入
输入只有那个不成立的等式,且等号两边的整数均不会超过
2000000000。
输出
输出只有一行。如果存在这样的方案,请输出那个正确的式子;如果
不存在解决方案,请输出“Impossible”(引号中的部分)。
样例输入 Copy
1234 = 492
样例输出 Copy
123 * 4 = 492
提示
测试数据保证不会出现多个解决方案

思路:
这个题和昨天那道整数拆分的题思想一样,但这道题还需要一层循环,首先先把等号右边的字符串转换成数字,然后一位位拆分左边的字符串,比较即可

string s;
ll x,p,q,n;
int main()
{
     
    cin >> s;
    p = 0,q = 0,x = 0;
    for(int i=0;i<s.size();i++)
    {
     
        if(s[i] == '=') n = i;      
    }
    for(int i=n+1;i<s.size();i++)
    {
     
        x = x*10+(s[i]-48);
    }
    for(int i=0;i<n;i++)
    {
     
        p = p*10+(s[i]-48);
        q = 0;
        for(int j=i+1;j<n;j++)
            q = q*10+(s[j]-48);
        if(p*q == x)
        {
     
            printf("%lld*%lld=%lld\n",p,q,x);
            return 0;
        }
    }
    printf("Impossible\n");
    return 0;
}

问题 C: 杨辉三角

题目描述
杨辉三角,又称贾宪三角形、帕斯卡三角形,由北宋人贾宪约于 1050 年在《释锁算术》中首先提出,下图显示的是杨辉三角的前 6 行。仔细研究杨辉三角,我们可以发现它的许多性质。
亲爱的同学,现在请你也来研究一下杨辉三角,并求出杨辉三角中第 m 行的第 n个数(按从左往右的顺序)。
upc 个人训练赛第六场:校门内的树(二分)_第1张图片

输入
只有一行,有二个整数m和n(m<=35),数间用一个空格隔开。
输出
只有一行,有一个整数,表示杨辉三角中第m行的第n个数。
样例输入 Copy
6 3
样例输出 Copy
10

思路:
写出一个杨辉三角的矩阵即可

int m,n;
ll a[40][40];
int main()
{
     
    cin >> m >> n;
    for(int i=1;i<=35;i++)
    {
     
        a[i][1] = a[i][i] = 1;
    }
    for(int i=3;i<=35;i++)
    {
     
        for(int j=2;j<=(i-1)/2+1;j++)
        {
     
            a[i][j] = a[i][i-j+1] = a[i-1][j]+a[i-1][j-1];
        }
    }
    printf("%lld\n",a[m][n]);
    return 0;
}

问题 D: 换座位

题目描述
聪聪和同学们正在玩这样一个换座位的游戏:班上共有2n个少先队员,开始时每个少先队员坐在自己的板凳上排成一队,由聪聪开始击鼓,每次击鼓开始时,前n个同学坐到第2、4、…、2n个板凳上,后n个同学坐到第1、3、…、2n-1个板凳上,击鼓结束时坐错或者还没有坐到对应板凳上的同学就要接受惩罚——表演一个节目,并按规定坐好。聪聪不断的击鼓然后停顿后又击鼓…,同学们都觉得这个游戏很好玩,但是当游戏结束时,同学们傻眼了,由于每位同学的板凳都差不多,他们找不到自己的板凳了。
而聪聪这时反应特别快,他说经过一定次数的换座位,每位同学一定能回到自己的板凳的。那么这个次数最少是多少呢?你会计算吗?
输入
共一行,一个正整数n(1≤n≤10000)。
输出
共一行,一个正整数,表示每位同学都回到自己板凳的最少换座位次数。
样例输入 Copy
10
样例输出 Copy
6

思路:
定义两个数组分别存储变换前和后的位置,模拟即可,注意数据范围

ll n,ans;
int a[maxn],b[maxn];
int judge(int b[])
{
     
    for(int i=1;i<=2*n;i++)
    {
     
        if(b[i] != i)   return 0;
    }
    return 1;
}
 
int main()
{
     
    n = read();
    for(int i=1;i<=2*n;i++)
    {
     
        a[i] = i;
        b[i] = i;
    }
    for(int k=0;;k++)
    {
     
        ans++;
        for(int i=1;i<=n;i++)    b[2*i] = a[i];
        for(int i=n+1;i<=2*n;i++)    b[(i-n)*2-1] = a[i];
        if(judge(b))
        {
     
            cout << ans << endl;
            return 0;
        }
        else
        {
     
            for(int i=1;i<=2*n;i++)
            a[i] = b[i];
        }
    }
    return 0;
}

问题 E: 神仙贷款

题目描述
神仙由于刚到凡间故手上缺钱,于是她去银行贷款了。因此,她在贷款之后,在一段时间内将不得不每月偿还固定的分期付款。这个问题要求计算神仙向银行支付的利率。假设利率按月累计。
输入
输入仅一行包含三个用空格隔开的正整数。第一个整数表示贷款的原值,第二个整数表示每月支付的分期付款金额,第三个整数表示分期付款还清贷款所需的总月数。
输出
输出一个实数,表示该贷款的月利率(用百分数表示),四舍五入精确到0.1%。
样例输入 Copy
1000 100 12
样例输出 Copy
2.9

思路:
题目说利率按月累计,也就是说假设利率是p,第一个月要还钱 = 每个月支付的分期付款金额 / (1+p),第二个月要还的钱 = 每个月支付的分期付款金额 / (1+p)^2,所以这样计算下来总金额
k
a = ∑ { b * [1 / (1+ans) ] ^ i }
i=1

化简之后可以得到这样一个式子 1 - a/b * p
很明显这个式子具有单调性,所以我们去二分这个答案
注意题目说要用百分数表示,并且保留一位小数

double n,m,k,l,r;
bool pd(double x)
{
     
    return (pow(1.0/(1.0+x),k)>=1-a/b*x);
}
int main()
{
     
    cin >> a >> b >> k;
    l = 0;r = 10;
    while(r-l >= 0.0001)
    {
     
        double mid = (l+r)/2;
        if(pd(mid)) r = mid;
        else l = mid;
    }
    printf("%.1f\n",l*100);
    return 0;
} 

你可能感兴趣的:(upc第一阶段训练,二分)