题目描述
假设母牛从出生起第4个年头开始每年可以生一头小母牛,但是第11年后死亡。按此规律,第n年时有多少头母牛?(假设n不大于30)
定义一个母牛类CCow,能够用动态创建和撤消类对象的方式来模拟小母牛的出生和死亡规律。试编写C++程序完成上述计算。
输入
第一行输入测试次数
每次测试输入一行,表示第几年的整数n(<=30)
输出
每次测试输出一行,第n年的母牛总数
IO模式
本题IO模式为标准输入/输出(Standard IO),你需要从标准输入流中读入数据,并将答案输出至标准输出流中。
输入样例:
3
7
30
25
输出样例:
6
28364
4530
代码示例:(两种实现)
① 用动态数组表示每年母羊信息,每个CCow对象表示当年母牛情况
#include <iostream>
using namespace std;
class CCow {
private:
int new_num; // 新增的母羊数量
int net; // 净增的母羊数量,等于新增减去世的数量
static int sum; // 静态变量,用于存储母羊的总数量
public:
// 构造函数,初始化新增母羊数量
CCow(int n) : new_num(n) {}
// 静态成员函数,设置母羊的总数量
static void setsum(int num) {
sum = num;
}
// 静态成员函数,获取母羊的总数量
static int getsum() {
return sum;
}
// 静态成员函数,重置母羊的总数量为1
static void resetsum() {
sum = 1;
}
// 成员函数,获取新增母羊数量
int get_new_num() {
return new_num;
}
};
// 初始化静态成员变量sum
int CCow::sum = 1;
// 主函数
int main() {
int t;
cin >> t; // 读取测试用例的数量
while (t--) {
int n;
cin >> n; // 读取每年的母羊数量
CCow** c = new CCow* [n]; // 创建动态数组,存储每年的母羊对象
// 初始化第一年的母羊对象,新增1只母羊
c[0] = new CCow(1);
// 接下来的两年没有新增母羊,总数保持为1
for (int i = 1; i < 3; i++) {
c[i] = new CCow(0);
CCow::setsum(1);
}
// 从第四年开始,每年有新增母羊
for (int i = 3; i < n; i++) {
int new_num = 0; // 当前年份的新增母羊数量初始化为0
int net; // 当前年份的净增母羊数量
// 遍历之前的每一年,累加新增母羊数量
for (int j = 0; j < i; j++) {
// 如果年份在第4年到第10年之间,则累加新增数量
if ((i - j) >= 3 && (i - j) < 10) {
new_num += c[j]->get_new_num();
}
}
// 创建新的CCow对象,代表当前年份的母羊增长情况
c[i] = new CCow(new_num);
// 如果年份小于11年,则净增等于新增
net = new_num;
// 如果年份从第11年开始,计算净增时需要减去10年前的新增数量
if (i >= 10) {
net = new_num - c[i - 10]->get_new_num();
}
// 更新母羊的总数量
int ssum = net + CCow::getsum();
CCow::setsum(ssum);
}
// 输出当前测试用例的母羊总数量
cout << CCow::getsum() << endl;
// 重置母羊总数量,为下一个测试用例做准备
CCow::resetsum();
// 释放动态分配的内存
for (int i = 0; i < n; i++) {
delete c[i];
}
delete[] c;
}
return 0;
}
主要思路:
一开始想的有点简单了,以为只要看它每年生了多少,再加上前一年总数就行,但是母羊也是会鼠的!
fine那就老老实实设两个变量,新增、净增。其中新增就是所有能生育的母羊数量(因为每只都生了一只),净增就是新增减去死亡的,然后CCow类表示羊群每一年的情况,用动态数组表示每一年的羊群情况,然后就能得到最后年份的母羊总数了。母羊总数用静态变量,关于母羊总数的设置、获取、重设都要用静态成员函数,注意每次结束后,要重设母羊总数!
表格如下,主要注意第十年,因为从这里开始可能会有羊死亡。
序号 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
新增 | 1 | 0 | 0 | 1 | 1 | 1 | 2 | 3 | 4 | 6 | 8 | 12 | 18 | 25 |
净增 | 1 | 0 | 0 | 1 | 1 | 1 | 2 | 3 | 4 | 6 | 7 | 12 | 18 | 24 |
总数 | 1 | 1 | 1 | 2 | 3 | 4 | 6 | 9 | 13 | 19 | 26 | 38 | 56 | 80 |
新增 = 年份符合的母羊数量
净增 = 今年新增 — 十年前的新增(即今年死亡)
总数 = 前一年总数 + 净增
代码里也有详细解析,供参考。只是为了快速过OJ的话,记得删干净,别被老师抓包了hh
【碎碎念】:感觉这道题还挺好玩的,像小学数学题,不过用编程实现的话,还是要理清下思路,写完对生死的觉悟又高了一层呢(bushi
参考代码② 怪怪的,老师后面补充说要用链表,不过还是没太懂老师意思,试了一下写
每个CCow对象代表一个母牛
#include <iostream>
using namespace std;
class CCow {
private:
int birthYear;
CCow* next;
static CCow* head;// 静态数据成员是链表头
public:
CCow(int year) : birthYear(year), next(nullptr) {}
static void sethead(int year) { head = new CCow(year); }
static void simulateYear(int year);
static int countCows();
static void del() ;
};
CCow* CCow::head = nullptr;
void CCow::simulateYear(int year) {
CCow* current = head;// 从头节点开始
CCow* p = nullptr;
// 遍历所有结点
while (current != nullptr) {
// p指向上一个,current指向当前
// 优先级:死亡 > 生育,所以先判断死亡
// 如果母牛年龄达到11年,则从链表中移除
if (year - current->birthYear >= 10) {
if (p != nullptr) {
p->next = current->next;
} else {
head = current->next;
}
delete current;
if (p != nullptr){
current = p->next;
}
else{
current = head;
}
continue; //已经死亡的母羊无法生育
}
// 如果母牛年龄达到4年,则生一头小母牛
if (year - current->birthYear >= 3) {
CCow* newCalf = new CCow(year);// 生小牛
// 下面两行实现头插法
newCalf->next = head;
head = newCalf;
}
p = current;
current = current->next;
}
}
int CCow::countCows() {
// 计算总数,只需遍历链表 计数即可
int count = 0;
CCow* current = head;
while (current != nullptr) {
count++;
current = current->next;
}
return count;
}
void CCow::del() {
// 切记delete掉每个结点
while (head != nullptr) {
CCow* temp = head;
head = head->next;
delete temp;
}
}
int main() {
int t;
cin >> t;
while (t--) {
int n;
cin >> n;
CCow::sethead(1);// 表头为第一年第一头羊
for (int year = 2; year <= n; ++year) {
// 依次模拟每一年
CCow::simulateYear(year);
}
cout << CCow::countCows() << endl;
CCow::del();
}
return 0;
}
思路:每个CCow对象是一个结点,表示一头有出生年份的小牛,那每一年的操作就是先去掉死亡的牛结点,再让可生育的牛生小牛结点,采用头插法。链表示意图:
tips: 两种方法对CCow类的理解不同,后者确实更科学点。不过都能完成任务就是了。