【暑期ACM热身赛第二场】

第一次写题解,对暑期集训比赛的总结与反思

目录

一.递推专题

Q - 一只小蜜蜂

R - 不容易系列之(3)―― LELE的RPG难题

S - 骨牌铺方格

T - 阿牛的EOF牛肉串

二.数学专题

J - 改革春风吹满地

 U - 神、上帝以及老天爷

V - 不容易系列之(4)――考新郎

 W - 折线分割平面

三.基础熟记

E-进制转换

I - 人见人爱A^B


一.递推专题

Q - 一只小蜜蜂

题目描述 有一只经过训练的蜜蜂只能爬向右侧相邻的蜂房,不能反向爬行。请编程计算蜜蜂从蜂房a爬到蜂房b的可能路线数。
其中,蜂房的结构如下所示。

输入
输入数据的第一行是一个整数N,表示测试实例的个数,然后是N 行数据,每行包含两个整数a和b(0<a<b<50)。

输出
对于每个测试实例,请输出蜜蜂从蜂房a爬到蜂房b的可能路线数,每个实例的输出占一行。

输入样例
2
1 2
3 6

输出样例
1
3

分析

斐波拉契数列变式:到达目标蜂房有且只有两条路径,故f[n]=f[n-1]+f[n-2],以1为递推边界

例如3到6可等效为1到4,故可得a到b为f[a-b+1]

 代码如下:

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

int main(){
	int n,m,a,b;
	long long fun[55];
	cin>>n;
	getchar();
	for(int i=1;i<=n;i++){
		memset(fun,0,sizeof(fun));
		fun[1]=1;
		fun[2]=1;
		for(int j=3;j<=55;j++)fun[j]=fun[j-1]+fun[j-2];
		cin>>a>>b;
		cout<<fun[b-a+1]<<endl;
	}
}

R - 不容易系列之(3)―― LELE的RPG难题

题目描述
人称“AC女之杀手”的超级偶像LELE最近忽然玩起了深沉,这可急坏了众多“Cole”(LELE的粉丝,即"可乐"),经过多方打探,某资深Cole终于知道了原因,原来,LELE最近研究起了著名的RPG难题:

有排成一行的n个方格,用红(Red)、粉(Pink)、绿(Green)三色涂每个格子,每格涂一色,要求任何相邻的方格不能同色,且首尾两格也不同色.求全部的满足要求的涂法.

以上就是著名的RPG难题.

如果你是Cole,我想你一定会想尽办法帮助LELE解决这个问题的;如果不是,看在众多漂亮的痛不欲生的Cole女的面子上,你也不会袖手旁观吧?

输入
输入数据包含多个测试实例,每个测试实例占一行,由一个整数N组成,(0<n<=50)。

输出
对于每个测试实例,请输出全部的满足要求的涂法,每个实例的输出占一行。

输入样例
1
2

输出样例
3
6
分析

递推典例:1.设定状态:f[n]为与相邻方格不同色且首尾不同色的最多图法

2.状态分界点:若第n-1与首同色,当前n有两种选择,从f[n-2]转移(n-1不满足状态)

若第n-1不与首同色,当前n只能选择一种颜色,从f[n-1]转移

3.状态转移方程:f[n]=f[n-1]+2*f[n-2]

代码如下:

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

int main()
{
    long long  n, i, b, j, m, fact;
    long long a[100];
    while(scanf("%lld", &n) != EOF)
        {
          a[1] = 3;
          a[2] = 6;
          a[3] = 6;

          for(i = 4; i <= n; i++)
            a[i] = a[i - 1] + 2*a[i - 2];
          printf("%lld\n", a[n]);
        }

    return 0;
}

S - 骨牌铺方格

题目描述
在2×n的一个长方形方格中,用一个1× 2的骨牌铺满方格,输入n ,输出铺放方案的总数.
例如n=3时,为2× 3方格,骨牌的铺放方案有三种,如下图:

输入
输入数据由多行组成,每行包含一个整数n,表示该测试实例的长方形方格的规格是2×n (0<n<=50)。

输出
对于每个测试实例,请输出铺放方案的总数,每个实例的输出占一行。

输入样例
1
3
2

输出样例
1
3
2

分析

由于宽度是确定的,所以以长度坐标为递推变量,每放一次(状态转移一次)只有两个横着放或者一个竖着放,故可得斐波拉契:f[n]=f[n-1]+f[n-2]

代码如下:

#include <iostream>
#include <string.h>
using namespace std;
 
int main(int argc, char** argv) {
    long long dp[55];
    dp[1] = 1;
    dp[2] = 2;
    dp[3] = 3;
    for(int i = 4; i <= 50; i++ ){
    	dp[i] = dp[i - 1] + dp[i - 2];
	}
	int n;
	while(cin >> n){
		cout << dp[n] << endl;
	}
    return 0;
}

T - 阿牛的EOF牛肉串

题目描述
今年的ACM暑期集训队一共有18人,分为6支队伍。其中有一个叫做EOF的队伍,由04级的阿牛、XC以及05级的COY组成。在共同的集训生活中,大家建立了深厚的友谊,阿牛准备做点什么来纪念这段激情燃烧的岁月,想了一想,阿牛从家里拿来了一块上等的牛肉干,准备在上面刻下一个长度为n的只由"E" “O” "F"三种字符组成的字符串(可以只有其中一种或两种字符,但绝对不能有其他字符),阿牛同时禁止在串中出现O相邻的情况,他认为,"OO"看起来就像发怒的眼睛,效果不好。

你,NEW ACMer,EOF的崇拜者,能帮阿牛算一下一共有多少种满足要求的不同的字符串吗?

PS: 阿牛还有一个小秘密,就是准备把这个刻有 EOF的牛肉干,作为神秘礼物献给杭电五十周年校庆,可以想象,当校长接过这块牛肉干的时候该有多高兴!这里,请允许我代表杭电的ACMer向阿牛表示感谢!

再次感谢!

输入
输入数据包含多个测试实例,每个测试实例占一行,由一个整数n组成,(0<n<40)。

输出
对于每个测试实例,请输出全部的满足要求的涂法,每个实例的输出占一行。

输入样例
1
2

输出样例
3
8

分析

1.设定状态:a[i]为前i个字符串满足要求的全部图法的个数

2.状态分界点:本来想正推,发现如果以n-1为E、F/O为分界点,EF可推出2*f[n-2],O的话无法确定前后状态,故采用逆推法

以当前字符为分界点,若为O,则n-1为E或F,n-2状态可确定;若为E/F,则n-1可确定

3.状态转移:a[i]=a[i-2]*2+a[i-1]*2

 代码如下:

#include<stdio.h>
int main(void)
{
    long long a[100];
    a[1]=3;
    a[2]=8;
    for(int i=3;i<=50;i++)
    {
        a[i]=2*a[i-1]+2*a[i-2];
    }
    int n;
    while(~scanf("%d",&n))
    {
        printf("%lld\n",a[n]);
    }
}

二.数学专题

J - 改革春风吹满地

题目描述
“ 改革春风吹满地,
不会AC没关系;
实在不行回老家,
还有一亩三分地。
谢谢!(乐队奏乐)”

话说部分学生心态极好,每天就知道游戏,这次考试如此简单的题目,也是云里雾里,而且,还竟然来这么几句打油诗。
好呀,老师的责任就是帮你解决问题,既然想种田,那就分你一块。
这块田位于浙江省温州市苍南县灵溪镇林家铺子村,多边形形状的一块地,原本是linle 的,现在就准备送给你了。不过,任何事情都没有那么简单,你必须首先告诉我这块地到底有多少面积,如果回答正确才能真正得到这块地。
发愁了吧?就是要让你知道,种地也是需要AC知识的!以后还是好好练吧…

输入
输入数据包含多个测试实例,每个测试实例占一行,每行的开始是一个整数n(3<=n<=100),它表示多边形的边数(当然也是顶点数),然后是按照逆时针顺序给出的n个顶点的坐标(x1, y1, x2, y2… xn, yn),为了简化问题,这里的所有坐标都用整数表示。
输入数据中所有的整数都在32位整数范围内,n=0表示数据的结束,不做处理。

输出
对于每个测试实例,请输出对应的多边形面积,结果精确到小数点后一位小数。
每个实例的输出占一行。

输入样例
3 0 0 1 0 0 1
4 1 0 0 1 -1 0 0 -1
0

输出样例
0.5
2.0

分析
根据s=1/2absinc,可以利用向量叉乘,设A(x1,y1)B(x2,y2) C(x3,y3),那么此时AB为(x2-x1,y2-y1)AC为(x3-x1,y3-y1),叉乘结果为S = 1/2 * |(x2 - x1) * (y3-y1) - (y2 - y1) * (x3 - x1)|即S = 1/2 * |(x1y2 + x2y3 + x3y1 - x1y3 - x2y1 - x3y2)|由于为多边形面积,可设某点为原点,逆时针每个边组成三角形求和即可。

更简洁的方法,设原点o为一点,依次遍历所以边求和即可,此时s三角形为1/2(x1y2-x2y1)

代码如下

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
int main()
{
    int t;
    while(cin>>t)
    {
        if(t==0) break;
        int x,y,x0,y0,sum=0;
        cin>>x>>y;
        x0=x;
        y0=y;
        while(--t)
        {
            int xtemp,ytemp;
            cin>>xtemp>>ytemp;
            sum+=(x*ytemp-y*xtemp);
            x=xtemp;
            y=ytemp;
        }
        sum+=(x*y0-y*x0);
        printf("%.1lf\n",abs(sum)/2.0);
    }
    return  0;
}/*多边形公式 

 U - 神、上帝以及老天爷

题目描述
HDU 2006’10 ACM contest的颁奖晚会隆重开始了!
为了活跃气氛,组织者举行了一个别开生面、奖品丰厚的抽奖活动,这个活动的具体要求是这样的:

首先,所有参加晚会的人员都将一张写有自己名字的字条放入抽奖箱中;
然后,待所有字条加入完毕,每人从箱中取一个字条;
最后,如果取得的字条上写的就是自己的名字,那么“恭喜你,中奖了!”

大家可以想象一下当时的气氛之热烈,毕竟中奖者的奖品是大家梦寐以求的Twins签名照呀!不过,正如所有试图设计的喜剧往往以悲剧结尾,这次抽奖活动最后竟然没有一个人中奖!

我的神、上帝以及老天爷呀,怎么会这样呢?

不过,先不要激动,现在问题来了,你能计算一下发生这种情况的概率吗?

不会算?难道你也想以悲剧结尾?!

输入
输入数据的第一行是一个整数C,表示测试实例的个数,然后是C 行数据,每行包含一个整数n(1<n<=20),表示参加抽奖的人数。

输出
对于每个测试实例,请输出发生这种情况的百分比,每个实例的输出占一行, 结果保留两位小数(四舍五入),具体格式请参照sample output。

输入样例
1
2

输出样例
50.00%
分析

错排:对于当前前n个进行错排,第n个可由(n-1)个位置可选,若选了k位置,则k的去向可作为状态转移分界点:1.k占了n的位置,则剩下(n-2)个位置进行错排 2.k不占n的位置,则k可选剩下(n-1)个位置,其余元素均可选(n-1)个位置,则可转移到f[n-1]状态;故可得

错排公式:f[n]=(n-1)*(f[n-1]+f[n-2]);

 代码如下

#include<bits/stdc++.h> 
using namespace std;
const int maxn=25;
long long D[maxn],N[maxn];
int main(){
	int c,x;
	cin>>c;
	D[0] = 0, N[0] = 1; //递推边界
	D[1] = 0, N[1] = 1;
	D[2] = 1, N[2] = 2;
	for (int i = 3; i < maxn; i++)
	{
		D[i] = (i - 1)*(D[i - 1] + D[i - 2]);///错排递推关系式
		N[i] = N[i - 1] * i; //求n!
	}
	for(int i=1;i<=c;i++){
		cin>>x;
		double sum=(D[x]*100.0/N[x]); 
		printf("%.2lf",sum);
		cout<<"%"<<endl;
	}
}

V - 不容易系列之(4)――考新郎

题目描述
国庆期间,省城HZ刚刚举行了一场盛大的集体婚礼,为了使婚礼进行的丰富一些,司仪临时想出了有一个有意思的节目,叫做"考新郎",具体的操作是这样的:

首先,给每位新娘打扮得几乎一模一样,并盖上大大的红盖头随机坐成一排;
然后,让各位新郎寻找自己的新娘.每人只准找一个,并且不允许多人找一个.
最后,揭开盖头,如果找错了对象就要当众跪搓衣板…

看来做新郎也不是容易的事情…

假设一共有N对新婚夫妇,其中有M个新郎找错了新娘,求发生这种情况一共有多少种可能.

输入
输入数据的第一行是一个整数C,表示测试实例的个数,然后是C行数据,每行包含两个整数N和M(1<M<=N<=20)。

输出
对于每个测试实例,请输出一共有多少种发生这种情况的可能,每个实例的输出占一行。

输入样例
2
2 2
3 2

输出样例
1
分析

错排思路同上,本题考到排列组合:求C(n,m)组合数 = n!/(m!*(n-m)!) n!同样利用递推公式f[n]=f[n-1]*n求出;

 代码如下:

#include<iostream>  //这道题的本质就是求解排列组合C(n,m)与错排m个元素D(m)的乘积
#include<cstdio>
using namespace std;
const int maxn = 25;
long long D[maxn], N[maxn]; /// D[i]表示错排数,N[i]表示全排列数(即n!)
int main()
{
	D[0] = 0, N[0] = 1; //递推边界
	D[1] = 0, N[1] = 1;
	D[2] = 1, N[2] = 2;
	for (int i = 3; i < maxn; i++)
	{
		D[i] = (i - 1)*(D[i - 1] + D[i - 2]);///错排递推关系式
		N[i] = N[i - 1] * i; //求n!
	}
	int T, n, m;
	cin >> T;
	while (T--)
	{
		cin >> n >> m;
		long long a = N[n] / (N[m] * N[n - m]); ///求C(n,m)组合数   n!/(m!*(n-m)!)
		cout << D[m] * a << endl;
	}
	return 0;
}

 W - 折线分割平面

题目描述
我们看到过很多直线分割平面的题目,今天的这个题目稍微有些变化,我们要求的是n条折线分割平面的最大数目。比如,一条折线可以将平面分成两部分,两条折线最多可以将平面分成7部分,具体如下所示。

输入
输入数据的第一行是一个整数C,表示测试实例的个数,然后是C 行数据,每行包含一个整数n(0<n<=10000),表示折线的数量。

输出
对于每个测试实例,请输出平面的最大分割数,每个实例的输出占一行。

输入样例
2
1
2

输出样例
2
7

分析

根据欧拉公式:在平面上增加一条直线,增加的平面区数=该直线经过的直线数+1

如果想增加最多平面,第n条线需要经过n-1条线,此时增加平面数=n-1+1

故可得:f[n]=f[n-1]+n=1+1+2+3....+n=(n+1)n/2+1

那么第n对直线则为2n,此时f[2n]=2n*n+n+1

而由于一对折线相对于一对直线少了两条射线,平面分割必少2*n份,故f[n]=2n*n+n+1-2n

=2n*n-n+1

 代码如下:

#include<stdio.h>
#include<string.h>
long long a[10001];
void ti()
{
    long long i;
    for(i=0;i<=10001;i++)
        a[i]=2*i*i-i+1;
}
int main()
{
    int t;
    scanf("%d",&t);
    ti();
    while(t--)
    {
        int n;
        scanf("%d",&n);
        printf("%lld\n",a[n]);
    }
    return 0;
}

三.基础熟记

E-进制转换

题目描述

输入一个十进制数N,将它转换成R进制数输出。

输入

输入数据包含多个测试实例,每个测试实例包含两个整数N(32位整数)和R(2<=R<=16, R<>10)

输出

为每个测试实例输出转换后的数,每个输出占一行。 如果R大于10,则对应的数字规则参考16进制(比如,10用A表示,等等)。

输入样例

7 2 

2 3

1 2

输出样例

111

1B

-11

代码如下:

#include<bits/stdc++.h>
using namespace std;
void binary(int n, int r)
{
    int m;
    if(n==0)
        return;
    else
    {
        binary(n/r, r);
        m=n%r;
        if(m<10)
            printf("%d", m);
        else
            printf("%c", 'A'+m-10);
    }
}


int main()
{
    int N, R;
    while(scanf("%d %d", &N, &R)!=EOF)
    {
        if(N==0)
            printf("%d",0);
        else if(N<0)
        {
            printf("-");
            N=-N;
        }
        binary(N, R);
        printf("\n");
    }
    return 0;
}

熟记:辗转相除法+字符转换

I - 人见人爱A^B

题目描述

求A^B的最后三位数表示的整数。
说明:A^B的含义是“A的B次方”

输入

输入数据包含多个测试实例,每个实例占一行,由两个正整数A和B组成(1<=A,B<=10000),如果A=0, B=0,则表示输入数据的结束,不做处理。

输出

为每个测试实例输出转换后的数,每个输出占一行。如果R大于10,则对应的数字规则参考16进制(比如,10用A表示,等等)。

输入样例

2 3 

12 6

6789 10000

0 0

输出样例

8

984 

1

代码如下:

#include<iostream>
#include<string>
#include<algorithm>
#include<vector>
using namespace std;
int main()
{
	long long n, m;
	while (cin >> n >> m)
	{
		if (n == 0 && m == 0)
			break;
		int x = n;
		for (int i = 1; i < m; i++)
		{
			n = (n % 1000) * x;
		}
		cout << n % 1000 << endl;
	}
	return 0;
}

熟记:取余相乘后再取余与原数取余的结果一样

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值