八年级上学期 C++编程期中考[1242:网线主管][1252:走迷宫][1281:最长上升子序列][1944:【08NOIP 普及组】传球游戏]

1242:网线主管


时间限制: 1000 ms         内存限制: 65536 KB
提交数: 22016     通过数: 5145

【题目描述】

仙境的居民们决定举办一场程序设计区域赛。裁判委员会完全由自愿组成,他们承诺要组织一次史上最公正的比赛。他们决定将选手的电脑用星形拓扑结构连接在一起,即将它们全部连到一个单一的中心服务器。为了组织这个完全公正的比赛,裁判委员会主席提出要将所有选手的电脑等距离地围绕在服务器周围放置。

为购买网线,裁判委员会联系了当地的一个网络解决方案提供商,要求能够提供一定数量的等长网线。裁判委员会希望网线越长越好,这样选手们之间的距离可以尽可能远一些。

该公司的网线主管承接了这个任务。他知道库存中每条网线的长度(精确到厘米),并且只要告诉他所需的网线长度(精确到厘米),他都能够完成对网线的切割工作。但是,这次,所需的网线长度并不知道,这让网线主管不知所措。

你需要编写一个程序,帮助网线主管确定一个最长的网线长度,并且按此长度对库存中的网线进行切割,能够得到指定数量的网线。

【输入】

第一行包含两个整数N和K,以单个空格隔开。N(1 ≤ N ≤ 10000)是库存中的网线数,K(1 ≤ K ≤ 10000)是需要的网线数量。

接下来N行,每行一个数,为库存中每条网线的长度(单位:米)。所有网线的长度至少1m,至多100km。输入中的所有长度都精确到厘米,即保留到小数点后两位。

【输出】

网线主管能够从库存的网线中切出指定数量的网线的最长长度(单位:米)。必须精确到厘米,即保留到小数点后两位。

若无法得到长度至少为1cm的指定数量的网线,则必须输出“0.00”(不包含引号)。

【输入样例】

4 11
8.02
7.43
4.57
5.39

【输出样例】

2.00

我的代码 (枚举):

#include<bits/stdc++.h>
using namespace std;
int n;
int k;
int x=1;
int i;
int cut;
double mlen=0;
double lens[10100];
int main()
{
	cin>>n>>k;
	for(i=0;i<n;i++)
	{
		scanf("%lf",&lens[i]);
		lens[i]*=100;
	}
	while(1)
	{
		cut=0;
		for(i=0;i<n;i++)
		{
			cut+=lens[i]/x;
		}
		if(cut>=x)
		{
			if(x>mlen)
			{
				mlen=x;
			}
				x++;
			}
			else
			{
				break;
			}
	}
	printf("%.2lf\n",mlen/100);
	return 0;
}

未通过

测试点 结果 内存 时间
测试点1 答案错误 616KB 3MS
测试点2 答案错误 632KB 5MS
测试点3 答案错误 628KB 4MS
测试点4 运行超时 660KB 998MS
测试点5 答案错误 628KB 3MS
测试点6 答案错误 628KB 4MS
测试点7 运行超时 664KB 997MS
测试点8 答案正确 704KB 15MS
测试点9 运行超时 668KB 1000MS
测试点10 运行超时 644KB 1003MS
测试点11 运行超时 660KB 1001MS
测试点12 答案错误 664KB 10MS
测试点13 答案错误 620KB 4MS

很显然,超时了!

解析:

需要等长网线数量已知,要求尽可能长,可以枚举所有网线可能的长度len∈[0, max] , 计算出每种长度下网线的数量,时间复杂度为 O(max*n)。以样例数据为例,已知库存网线4根,长度分别是8.02, 7.43, 4.57, 5.39。现在需要11根等长的网线,尽可能长。从输出数据看,长度为2.00,也就是说,第一根分割出4根,第二根分割出3根,第三根分割出2根,第四根分割出2根,共11根。
枚举法,O(max*n)超时

方法二(递归):

代码(附上注释)

/*
提醒:
1,本道题目用二分解决(我不清楚用其他的因为我就是用的是二分)
2,注意精度问题
3,精确到厘米所以只要把输入乘以100储存到数组里即可(由题中所得的:若无法得到长度至少为1cm的指定数量的网线,则必须输出“0.00”(不包含引号)。) 
*/
#include<bits/stdc++.h>
int n,k; 
int a[10200];//由于精度问题,把输入数据*100放入整数数组
int l=0, mid, r=0;
double t;
void erfen()
{
	scanf("%d%d", &n, &k);
	for (int i=1;i<=n;i++) 
	{
		scanf("%lf", &t);
		a[i] = (float)(t*100.0);
		if(a[i]>r)
		{
			r=a[i];
		}
	} 
	r++;
}
int check(int x) 
{
	int ans=0;
	for (int i=1;i<=n;i++) 
	{
		ans += a[i] / x;
	}
	if(ans>=k)
	{
		return 1;
	} 
	else 
	{
		return 0; 
	}
}
void me()
{
	while (l+1<r) //r要在最长电线上+1,因为会出现整条电线用完的情况
	{
		mid = (l+r)/2;
		if (check(mid)) 
		{
			l=mid;
		}
		else  
		{
			r=mid;
		}
	}
	printf("%.2lf", l / 100.0); 
}
int main() 
{	
	erfen();
	me();
	return 0;
}

去掉注释的代码(我知道某些人很喜欢在CSDN上找这样的代码):

#include<bits/stdc++.h>
int n,k; 
int a[10200];
int l=0, mid, r=0;
double t;
void erfen()
{
	scanf("%d%d", &n, &k);
	for (int i=1;i<=n;i++) 
	{
		scanf("%lf", &t);
		a[i] = (float)(t*100.0);
		if(a[i]>r)
		{
			r=a[i];
		}
	} 
	r++;
}
int check(int x) 
{
	int ans=0;
	for (int i=1;i<=n;i++) 
	{
		ans += a[i] / x;
	}
	if(ans>=k)
	{
		return 1;
	} 
	else 
	{
		return 0; 
	}
}
void me() {
	while (l+1<r) 
	{
		mid = (l+r)/2;
		if (check(mid)) 
		{
			l=mid;
		}
		else  
		{
			r=mid;
		}
	}
	printf("%.2lf", l / 100.0); 
}
int main() 
{	
	erfen();
	me();
	return 0;
}

结果:通过
 

测试点 结果 内存 时间
测试点1 答案正确 608KB 3MS
测试点2 答案正确 600KB 7MS
测试点3 答案正确 620KB 7MS
测试点4 答案正确 644KB 7MS
测试点5 答案正确 620KB 4MS
测试点6 答案正确 612KB 4MS
测试点7 答案正确 636KB 7MS
测试点8 答案正确 644KB 6MS
测试点9 答案正确 644KB 7MS
测试点10 答案正确 632KB 6MS
测试点11 答案正确 640KB 7MS
测试点12 答案正确 628KB 5MS
测试点13 答案正确 616KB 4MS

1281:最长上升子序列


时间限制: 1000 ms         内存限制: 65536 KB
提交数: 23765     通过数: 12492

【题目描述】

一个数的序列bibi,当b1<b2<...<bSb1<b2<...<bS的时候,我们称这个序列是上升的。对于给定的一个序列(a1,a2,...,aN)(a1,a2,...,aN),我们可以得到一些上升的子序列(ai1,ai2,...,aiK)(ai1,ai2,...,aiK),这里1≤i1<i2<...<iK≤N1≤i1<i2<...<iK≤N。比如,对于序列(1,7,3,5,9,4,8),有它的一些上升子序列,如(1,7),(3,4,8)等等。这些子序列中最长的长度是4,比如子序列(1,3,5,8)。

你的任务,就是对于给定的序列,求出最长上升子序列的长度。

【输入】

输入的第一行是序列的长度N(1≤N≤1000)。第二行给出序列中的N个整数,这些整数的取值范围都在0到10000。

【输出】

最长上升子序列的长度。

【输入样例】

7
1 7 3 5 9 4 8

【输出样例】

4

这道题是模型题,必须掌握

十分代码(经常出错的地方)

#include<bits/stdc++.h>
using namespace std;
int n;
int ans;
int a[1010];
int b[1010];
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
		b[i]=1;
	}
	for(int i=2;i<=n;i++)
	{
		for(int j=1;j<i;j++)
		{
			if(a[i]>a[j])
			{
				b[i]=max(b[j]+1,b[i]);
			}
			else
			{
				ans=max(ans,b[i]);
			}
		}
	}
	cout<<ans<<endl;
	return 0;
}

 结果:

未通过
 

测试点 结果 内存 时间
测试点1 答案正确 612KB 7MS
测试点2 答案错误 608KB 7MS
测试点3 答案错误 612KB 7MS
测试点4 答案错误 600KB 6MS
测试点5 答案错误 600KB 4MS
测试点6 答案错误 600KB 5MS
测试点7 答案错误 604KB 6MS
测试点8 答案错误 608KB 5MS
测试点9 答案错误 608KB 5MS
测试点10 答案错误 612KB 6MS

不通过原因:

满分代码(开始全面讲解){先不加注释,为了是给那些喜欢这样子的代码一个方便}

#include<bits/stdc++.h>
using namespace std;
int n;
int ans;
int a[1010];
int b[1010];
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
		b[i]=1;
	}
	for(int i=2;i<=n;i++)
	{
		for(int j=1;j<i;j++)
		{
			if(a[i]>a[j])
			{
				b[i]=max(b[j]+1,b[i]);
			}
			else b[i]=b[i];
			ans=max(ans,b[i]);
		}
	}
	cout<<ans<<endl;
	return 0;
}

 做题时的参考:

我看了很多博主的代码(此处为转载)

比如:

CSDN中的大神:

君义_noip

我看了他写的代码有四个方法:

1,状态定义为以i为结尾的最长上升子序列的长度

2,状态定义为以i为起始的最长上升子序列的长度

3,手写二分查找

4,使用stl lower_bound

原转载:

君义_noip大神这道题题解

我个人认为像这样的模型题不应该把代码写得这么长

这道题应该找到状态转移方程写,更好些(此处一个憨憨的微笑){我看CSDN里的大神博客很少用这种方法}

我个人做dp题目,都会考虑状态转移方程(可能有些大神有更好的方法,但状态转移方程是我做题的一个思路)

本题的状态转移方程为:(相信很多人看了我的源代码就知道了)

b[i]=max(b[j]+1,b[i]);

代码:(带注释)

#include<bits/stdc++.h>
using namespace std;
int n;
int ans;
int a[1010];
int b[1010];
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
		b[i]=1;
	}
	for(int i=2;i<=n;i++)
	{
		for(int j=1;j<i;j++)
		{
			if(a[i]>a[j])
			{
				b[i]=max(b[j]+1,b[i]);//状态转移方程 
			}
			else b[i]=b[i];
			ans=max(ans,b[i]);
		}
	}
	cout<<ans<<endl;
	return 0;
}

结果:通过
 

测试点 结果 内存 时间
测试点1 答案正确 604KB 4MS
测试点2 答案正确 604KB 4MS
测试点3 答案正确 604KB 4MS
测试点4 答案正确 608KB 4MS
测试点5 答案正确 608KB 4MS
测试点6 答案正确 600KB 5MS
测试点7 答案正确 596KB 4MS
测试点8 答案正确 608KB 5MS
测试点9 答案正确 604KB 5MS
测试点10 答案正确 604KB 5MS

1252:走迷宫


时间限制: 1000 ms         内存限制: 65536 KB
提交数: 20939     通过数: 9571

【题目描述】

一个迷宫由R行C列格子组成,有的格子里有障碍物,不能走;有的格子是空地,可以走。

给定一个迷宫,求从左上角走到右下角最少需要走多少步(数据保证一定能走到)。只能在水平方向或垂直方向走,不能斜着走。

【输入】

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值