PTA基础模拟+结构体的应用(DeBug:算法实现与算法设计的不一致)

L1-7 分寝室 - 第一套 2023年天梯赛真题 (pintia.cn)

学校新建了宿舍楼,共有 n 间寝室。等待分配的学生中,有女生 n0​ 位、男生 n1​ 位。所有待分配的学生都必须分到一间寝室。所有的寝室都要分出去,最后不能有寝室留空。
现请你写程序完成寝室的自动分配。分配规则如下:

  • 男女生不能混住;
  • 不允许单人住一间寝室;
  • 对每种性别的学生,每间寝室入住的人数都必须相同;例如不能出现一部分寝室住 2 位女生,一部分寝室住 3 位女生的情况。但女生寝室都是 2 人一间,男生寝室都是 3 人一间,则是允许的;
  • 在有多种分配方案满足前面三项要求的情况下,要求两种性别每间寝室入住的人数差最小。

输入格式:

输入在一行中给出 3 个正整数 n0​、n1​、n,分别对应女生人数、男生人数、寝室数。数字间以空格分隔,均不超过 105。

输出格式:

在一行中顺序输出女生和男生被分配的寝室数量,其间以 1 个空格分隔。行首尾不得有多余空格。
如果有解,题目保证解是唯一的。如果无解,则在一行中输出 No Solution

输入样例 1:

24 60 10

输出样例 1:

4 6

注意:输出的方案对应女生都是 24/4=6 人间、男生都是 60/6=10 人间,人数差为 4。满足前三项要求的分配方案还有两种,即女生 6 间(都是 4 人间)、男生 4 间(都是 15 人间);或女生 8 间(都是 3 人间)、男生 2 间(都是 30 人间)。但因为人数差都大于 4 而不被采用。

输入样例 2:

29 30 10

输出样例 2:

No Solution

感谢浙江警官职业学院楼满芳老师斧正数据!

代码长度限制

16 KB

时间限制

400 ms

内存限制

64 MB

栈限制

8192 KB

初始代码: 

#include<iostream>
using namespace std;
int n0,n1,n;
int x,y;
int a,b;
int chazhi;
int ans=-1;
int main(){
    cin>>n0>>n1>>n;
    chazhi=max(n0,n1);//差值不可能超过n0,或n1
    for(int i=2;i<n0/2;i++){//从两间开始遍历
        if(n0%i==0){
            if(n1%(n-i)==0){
                a=n0/i;//女生每间人数
                b=n1/(n-i);//男生每间人数
                if(a>1&&b>1){
                    int m_chazhi=abs(a-b);
                    if(m_chazhi<chazhi){
                        chazhi=m_chazhi;
                        ans=i;
                    }
                }
            }
        }
    }
    if(ans==-1)
        cout<<"No Solution"<<endl;
    else
        cout<<ans<<" "<<n-ans<<endl;
    return 0;
}

 优化判断结构(不影响结果):

#include <iostream>
using namespace std;
int n0, n1, n;
int chazhi;
int ans = -1;

int main() {
	cin >> n0 >> n1 >> n;
	chazhi = max(n0, n1); //差值不可能超过n0,或n1
	for (int i = 2; i < n0 / 2; i++) { //从两间开始遍历
		if (n0 % i == 0 && n1 % (n - i) == 0 && n0 / i > 1 && n1 / (n - i) > 1) {
			int m_chazhi = abs(n0 / i - n1 / (n - i));
			if (m_chazhi < chazhi) {
				chazhi = m_chazhi;
				ans = i;
			}
		}
	}
	if (ans == -1)
		cout << "No Solution" << endl;
	else
		cout << ans << " " << n - ans << endl;
	return 0;
}

每日一题 分寝室(C++完成)_学校新建了宿舍楼,共有 n 间寝室。等待分配的学生中,有女生 n 0 位、男生 n 1 位-CSDN博客

第一次修改后:利用数组存所有结果,最后排序

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int n0, n1, n;


int main() {
	cin >> n0 >> n1 >> n;
	vector<int> a;
	for (int i = 2; i < n0 / 2; i++) { //从两间开始遍历
		if (n0 % i == 0 && n1 % (n - i) == 0 && n0 / i > 1 && n1 / (n - i) > 1) {
			a.push_back( abs(n0 / i - n1 / (n - i)));
		}
	}
	sort(a.begin(), a.end());
	if (a.empty())
		cout << "No Solution" << endl;
	else
		cout << a[0] << " " << n - a[0] << endl;
	return 0;
}

第二次修改后:对比正确代码后,扩大遍历范围到n0(其实扩大到n0/2就行)

#include<iostream>
using namespace std;
int n0,n1,n;
int x,y;
int a,b;
int chazhi;
int ans=-1;
int main(){
    cin>>n0>>n1>>n;
    chazhi=max(n0,n1);//差值不可能超过n0,或n1
    for(int i=2;i<n0;i++){//从两间开始遍历,到n0,而不是一半
        if(n0%i==0){
            if(n1%(n-i)==0){
                a=n0/i;//女生每间人数
                b=n1/(n-i);//男生每间人数
                if(a>1&&b>1){
                    int m_chazhi=abs(a-b);
                    if(m_chazhi<chazhi){
                        chazhi=m_chazhi;
                        ans=i;
                    }
                }
            }
        }
    }
    if(ans==-1)
        cout<<"No Solution"<<endl;
    else
        cout<<ans<<" "<<n-ans<<endl;
    return 0;
}

问题就在范围这。 

优化判断结构:

#include <iostream>
using namespace std;
int n0, n1, n;
int chazhi;
int ans = -1;

int main() {
	cin >> n0 >> n1 >> n;
	chazhi = max(n0, n1); //差值不可能超过n0,或n1
	for (int i = 2; i < n0; i++) { //从两间开始遍历
		if (n0 % i == 0 && n1 % (n - i) == 0 && n0 / i > 1 && n1 / (n - i) > 1) {
			int m_chazhi = abs(n0 / i - n1 / (n - i));
			if (m_chazhi < chazhi) {
				chazhi = m_chazhi;
				ans = i;
			}
		}
	}
	if (ans == -1)
		cout << "No Solution" << endl;
	else
		cout << ans << " " << n - ans << endl;
	return 0;
}



现在问题是为什么第一次修改没有用:
为什么用数组存放,排序后输出最小的思路会错,就算是改好了范围问题后: 

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int n0, n1, n;


int main() {
	cin >> n0 >> n1 >> n;
	vector<int> a;
	for (int i = 2; i < n0; i++) { //从两间开始遍历到n0
		if (n0 % i == 0 && n1 % (n - i) == 0 && n0 / i > 1 && n1 / (n - i) > 1) {
			a.push_back( abs(n0 / i - n1 / (n - i)));
		}
	}
	sort(a.begin(), a.end());
	if (a.empty())
		cout << "No Solution" << endl;
	else
		cout << a[0] << " " << n - a[0] << endl;//要输出的不是差值!
	return 0;
}

重新捋一遍代码后,发现:
用来排序的根本不是要输出的输出的排序关键字不是自己。 

这种排序后,输出的不是关键字,而是与之相对应的其他属性,这种情况就要利用到结构体对多个属性的封装性,实现不同属性之间的捆绑。这也是结构体的妙处:一个变量包含多种捆绑在一起的信息。

此题要使用排序输出最小方案,就得将间数和人数差捆绑起来,pair、struct、map等都可以。

排序思路修改:

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;
int n0, n1, n;
typedef pair<int, int> node;

bool cmp(node i, node j) {
	return i.second < j.second; //排序标准,小的在前
}

int main() {
	cin >> n0 >> n1 >> n;
	vector<node> a;
	for (int i = 2; i < n0; i++) { //从两间开始遍历
		if (n0 % i == 0 && n1 % (n - i) == 0 && n0 / i > 1 && n1 / (n - i) > 1) {
			{
				node temp;
				temp.second = abs(n0 / i - n1 / (n - i));
				temp.first = i;
				a.push_back( temp );
			}
		}
	}
	sort(a.begin(), a.end(), cmp);
	if (a.empty())
		cout << "No Solution" << endl;
	else
		cout << a[0].first << " " << n - a[0].first << endl;
	return 0;
}

代码真的要步步有依据啊,跟数学题一样,步步不能错,步步要清晰,步步有依据。

看来,程序员的玩具鸭真有来头🤣🤣🤣 



现在,还有一个问题,为什么不是遍历到 n0/2 就结束呢?

这得看看自己的思路草稿:
自己考虑的是 i∈[2,n0/2],但是代码写的却是i<n0/2,将n0/2排除了🤣😁😁😁

只用一个变量迭代保存最小值(同时保存对应的 i )的思路:

#include<iostream>
using namespace std;
int n0,n1,n;
int x,y;
int a,b;
int chazhi;
int ans=-1;
int main(){
    cin>>n0>>n1>>n;
    chazhi=max(n0,n1);//差值不可能超过n0,或n1
    for(int i=2;i<=n0/2;i++){//从两间开始遍历到n0/2
        if(n0%i==0){
            if(n1%(n-i)==0){
                a=n0/i;//女生每间人数
                b=n1/(n-i);//男生每间人数
                if(a>1&&b>1){
                    int m_chazhi=abs(a-b);
                    if(m_chazhi<chazhi){
                        chazhi=m_chazhi;
                        ans=i;
                    }
                }
            }
        }
    }
    if(ans==-1)
        cout<<"No Solution"<<endl;
    else
        cout<<ans<<" "<<n-ans<<endl;
    return 0;
}

优化代码结构: 

#include <iostream>
using namespace std;
int n0, n1, n;
int chazhi;
int ans = -1;

int main() {
	cin >> n0 >> n1 >> n;
	chazhi = max(n0, n1); //差值不可能超过n0,或n1
	for (int i = 2; i <= n0 / 2; i++) { //从两间开始遍历
		if (n0 % i == 0 && n1 % (n - i) == 0 && n0 / i > 1 && n1 / (n - i) > 1) {
			int m_chazhi = abs(n0 / i - n1 / (n - i));
			if (m_chazhi < chazhi) {
				chazhi = m_chazhi;
				ans = i;
			}
		}
	}
	if (ans == -1)
		cout << "No Solution" << endl;
	else
		cout << ans << " " << n - ans << endl;
	return 0;
}

结构体数组排序思路:

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;
int n0, n1, n;
typedef pair<int, int> node;

bool cmp(node i, node j) {
	return i.second < j.second; //排序标准,小的在前
}

int main() {
	cin >> n0 >> n1 >> n;
	vector<node> a;
	for (int i = 2; i <= n0/2; i++) { //从两间开始遍历
		if (n0 % i == 0 && n1 % (n - i) == 0 && n0 / i > 1 && n1 / (n - i) > 1) {
			{
				node temp;
				temp.second = abs(n0 / i - n1 / (n - i));
				temp.first = i;
				a.push_back( temp );
			}
		}
	}
	sort(a.begin(), a.end(), cmp);
	if (a.empty())
		cout << "No Solution" << endl;
	else
		cout << a[0].first << " " << n - a[0].first << endl;
	return 0;
}

顿时……看来自己的代码还不一定就符合自己的思路,和上面数组排序不用结构体控制输出犯了同样性质的错误:自己的代码并没有完美地表达自己的意思。

一开始几分钟就能AC的事……,只要我范围不写错,这题立马直接过,而不是自己傻乎乎照着别人的思路写,自己还没领悟清反而给自己找了另外的bug,最后发现自己的思路没错,只是一个范围漏了一点。

而这就恰恰说明了,当自己代码出现问题时,自己没有静下心来,第一时间仔细检查自己的每一步代码及其依据,而是浮躁的、不自信的,直接去看别人的代码。

心得:

当自己的代码有问题,先静下心来,不要觉得不耐烦,自己本来就是以一个不耐烦的心理状况写的,代码自然就不严谨,问题自然就多,这是自己造成的,现在就应该好好理理思路,想清楚每一步代码的逻辑和依据,先看自己代码实现是否符合自己思路,再考虑思路问题。

总结一下:

  1. 用来排序的根本不是要输出的
  2. 自己考虑的是 i∈[2,n0/2],但是代码写的却是i<n0/2,将n0/2排除了🤣😁😁😁
  3. 连续的 if 嵌套可以考虑放在一个 if 里,避免程序没必要的分支
  4. 这种排序后,输出的不是关键字,而是与之相对应的其他属性,这种情况就要利用到结构体对多个属性的封装性,实现不同属性之间的捆绑。
  5. 写代码的时候有多认真,debug的时候就用多轻松,你要反过来?对不起,bug只会只增不减
  6. 检查代码,先检查算法实现是否正确(一步一步说出依据),再考虑算法本身是否有问题(小黄鸭debug法哈哈哈哈)

我是小丑……

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值