C实践习题集6

选座位

题目

已知公交车中有n排座位,每排都有2个座位。第i排的两个座位的宽度均为wi厘米。没有相同宽度的两排座位。

公共汽车最初是空的。有2n位乘客按顺序先后进入公共汽车。 乘客分为两种类型:

内向者:总是选择两个座位都没人的一排。在这些排中,他选择座位宽度最小的,并占据了其中的一个座位; 外向型:总是选择有人的一排。 在这些排中,他选择座位宽度最大的那个,并占据了空位。

你会得到每排座位的宽度和乘客进入公共汽车的顺序。 请你帮忙确定每位乘客将乘坐哪一排座位。

输入格式:

第一行包含一个整数n(1 ≤ n ≤ 200),表示公共汽车上座位的总排数。

第二行是一个包含n个整数的序列w 1,w 2,...,w n(1 ≤ w i ≤ 10000),其中wi是第i行中每个座位的宽度。 保证所有 w i 都不同。

第三行包含一个长度为 2n 的字符串,由数字“0”和“1”组成,该字符串描述了乘客进入公共汽车的顺序。 如果第j个字符是 '0',那么说明第 j 个进入公共汽车的乘客是内向型的;如果第j个字符是 '1',则表示第j个进入公交车的乘客是外向型的。 保证外向者的数量等于内向者的数量(即'0'和'1'的个数均为 n),并且对于每个外向者总是有合适的行。

输出格式:

打印 2n 个整数,整数之间以空格分隔,表示乘客将坐的排。 乘客的顺序应与输入的顺序相同。

输入样例1:

2
3 1
0011

输出样例1:

2 1 1 2

输入样例2:

6
10 8 9 11 13 5
010010011101

输出样例2:

6 6 2 3 3 1 4 4 1 2 5 5

思路

这题就栈的思想来解是极好,因为题目中涉及到配对问题,再结合题目所说的内向的人优先选位置小的,外向的人优先选有人且位置大的(其实就是栈顶)。
值得一提的是,这题在排座位大小时,还需要利用座位的宽度找到座位的最初编号,因为题目中座位的宽度给出了范围(1 ≤ w i ≤ 10000),且宽度各不相同于是我就开一个数组,以宽度作为数组的下
标,以最初编号作为元素值,这样,在排序完就能很容易找到某一宽度的座位对应的编号。
还有就是我很想吐槽,这题宽度明明最大就10000,我原本以为数组开10005就够了,没想到开到了1000005(抓狂)

完整代码

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int lar[210],num[1000005],temp[450];//lar数组用于储存作为大小,num数组 
struct pas{
	char c;//类型 
	int order;//所坐的排数 
}line[800];
int main()
{
	int n,i,j=0,top=-1;
	cin>>n;
	for(i=0;i<n;i++){
		cin>>lar[i];
		num[lar[i]]=i;//第i排的作为宽为lar[i] 
	}
	for(i=0;i<2*n;i++) cin>>line[i].c;
	sort(lar,lar+n);
	for(i=0;i<2*n;i++){
	    if(line[i].c=='0'){
		    temp[++top]=i;
			line[i].order=num[lar[j++]]; 
		}
		else{
			line[i].order=line[temp[top]].order;
			top--;
		}
	}
	for(i=0;i<2*n;i++) cout<<line[i].order+1<<' ';
	return 0;
} 

括号匹配调整

题目

如果通过插入“ +”和“ 1”可以从中得到格式正确的数学表达式,则将带括号的序列称为正确的。

例如,序列 "(())()","()"和 "(()(()))"是正确的,而")(","(()))("和"(()" 不是。

定义重新排序操作:选择括号序列的任意连续子段(子字符串),然后以任意方式对其中的所有字符进行重新排序。

当重新排序的子段的长度为t时,重新排序操作需要耗时t秒。

例如,对于“))((”,他可以选择子字符串“)(”并重新排序“)()(”(此操作将花费2秒)。

不难看出,重新排序操作不会改变左括号和右括号的数量。

现在,LD想花费最少的时间,通过任意次数(可能为零)执行重新排序操作来使括号序列变成正确的。

输入格式:

第一行包含一个整数n(1≤n≤1e6),表示序列的长度;

第二行包含一个长度为n的字符串,仅由字符‘(’和‘)’组成。

输出格式:

输出一个整数,表示使括号序列正确的最小秒数;如果不可能实现,则输出-1。

输入样例:

8
))((())(

输出样例:

6

思路

我现在看到这题就想骂脏话,题意理解错了,以为只要总括号数为2的倍数,即可利用重新排序操作将括号序列变为正确的。导致我改代码改了几个小时,做了无用功(得知真相的我:我#…&%…&%¥#%¥*&…)
所以其实题目要求实现起来很简单,需要利用重新排序的情况只有一种,就是栈顶为),而当前检索到的数组元素为(,此时利用重新排序,耗时2秒,并把当前栈顶元素弹出栈。

完整代码

#include<iostream>
using namespace std;
char str[1000005],temp[10000];
int main()
{
	int n,i,top=-1,sum=0;
	cin>>n;
	cin>>str;
	for(i=0;i<n;i++){
		if(-1==top) temp[++top]=str[i];
		else{
			if(temp[top]=='('&&str[i]==')') top--;
			else if(temp[top]==')'&&str[i]=='('){
				top--;
				sum+=2;
			}
			else temp[++top]=str[i];
		}
	}
	if(top!=-1){
		cout<<-1;
	}
	else cout<<sum;
	return 0;
} 

堆放石子

题目

有N堆石子,每堆石子有若干石头,所有石头的总数是N的倍数。

可以在任意一堆上取若干石头,进行移动。移动规则是:在第一堆上取的石子,只能移到第二堆;在第N堆上取的石子,只能移到N-1堆;其他堆上取的,可以移到相邻左边或者右边。如何用最少的移动次数使得每堆石子的数量一样多呢?

当N=4时,4堆石子数为:9、8、17、6

移动3次可以使4堆数目一样多:

从第3堆取4个石子放到第4堆(9、8、13、10)

从第3堆取3个放到第2堆(9、11、10、10)

从第2堆取1个放到第1堆(10、10、10、10)

输入格式:

第一行包含一个整数N(1<= N <=100),表示有N堆石子;

接着有N行,每行一个整数ai(1<= ai <=10000),表示第i堆石子的数量。

输出格式:

输出一个整数,表示使所有石子堆的石子均达到相等时需要的最少移动次数。

输入样例:

4
9 8 17 6

输出样例:

3

思路

典型的贪心算法题。%大佬

完整代码

#include<iostream>
using namespace std;
int main()
{
	int N,i,ave,n[105],sum=0,count=0;
	cin>>N;
	for(i=1;i<=N;i++){
		cin>>n[i];
		sum+=n[i];
	}
	ave=sum/N;
	for(i=1;i<N;i++){
		if(n[i]!=ave){
			n[i+1]+=n[i]-ave;
			count++;
		}
	}
	cout<<count;
	return 0;
} 

168

题目

汉堡包在大街上大摇大摆的走着,看着手机上一道难倒数万人的小学数学题:

1 + 1 = 0

1 + 6 = 1

6 + 6 = 2

8 + 1 = 2

8 + 6 = 3

汉堡包看完之后发现上面这些加法的答案就是看1,6,8中圈圈的个数嘛!

突然之间,所有大厦上的LED屏幕上的广告全部变成数字1,6,8三个数字的随机闪现。

现给你一块n*m的LED屏幕,上面有且仅有一个数字(1,6,or 8),请你输出你看见的那个字母。

输入格式:

第一行输入两个整数n,m(2<= m, n <= 1000);

接下来n行,每行由m个数字0和1组成,其中1表示数字1,6,8的组成部分。

输出格式:

输出一个整数,代表图形表示的数字。

输入样例:

7 7
0 0 0 0 0 0 0
0 0 1 1 1 0 0
0 0 1 0 1 0 0
0 0 1 1 1 0 0
0 0 1 0 1 0 0
0 0 1 1 1 0 0
0 0 0 0 0 0 0

输出样例:

8

此外,还有几个比较有趣的测试数据

//8
6 11
0 0 0 0 0 1 1 1 1 1 1
0 0 0 0 0 1 1 0 0 0 0
0 0 0 0 0 1 1 0 0 0 0
0 0 0 0 0 1 1 1 1 1 1
0 0 0 0 0 1 1 0 0 0 0
0 0 0 0 0 1 1 0 0 0 0
0 0 0 0 0 1 1 1 1 1 1

//8
9 11
00000000000
00000111111
00000110000
00000110000
00000111111
00000110000
00000110000
00000111111
00000000000

//6(目前的代码过不了这个测试数据)
8 7
0000000
0000010
0000100
0001000
0001000
0010100
0111110
0000000

//6
9 11
11111111111
11000000011
11000000011
11000000000
11111111111
11000000011
11000000011
11000000011
11111111111

//6
9 11
11111110000
11000000000
11000000000
11000000000
11111111111
11000000011
11000000011
11000000011
11111111111

//6
9 11
11111111111
11111111111
11111111111
11111111111
11111111111
11000000000
11111111111
11000000011
11111111111

思路

主要是判断6有几种情况,剩下的情况,只有一种是1,其他全是8.看行的话,每行1的个数设为x,然后1之间有0的话用f1标记为1,否则为0,然后一行行检索下来,从第一行开始,会有一个x值,然后如果到某行的时候,x变化了,此时比较之前的x和现在的x的大小,现在的比较小的话,就分析这行是否在1中有掺杂0,有的话肯定是8,没有就是6,这种情况要排除E情况,所以我得加一个特判,然后如果现在的x比较大,那肯定是6,如果检索完所有行,x都没变过,那肯定是1。
(这份代码我觉得还存在一些缺陷,有时间会试着继续改改)

#include<iostream>
using namespace std;
int a[1005][3];
char map[1005][1005];
int main()
{
	int n,m,i,j,len,s1=-1,s2,f,f1,f2=0,f3=0;//s1用来标记第一个非全0行的行编号,s2用于标记最后一个非全0行的位置
	//f用于标记第一个非全0行的最后一个1的位置,f1用于标记判断非全0行中1是否连续 
	//f2用于标记判断是否检索过所有行
	cin>>n>>m;
	for(i=0;i<n;i++){
		len=0;//某一行所含1的个数 
		f1=0;//用于标记判断是否掺杂0 
		for(j=0;j<m;j++){
			cin>>map[i][j];
			if('1'==map[i][j]){
				if(s1==-1) s1=i;//锁定首个非全0行 
				if(len!=0){
					if(map[i][j-1]=='0') f1=1;//检查前一个是否为0,即检查是否掺杂0 
				}
				len++;
			}
			if(i==s1&&map[i][j]=='1') f=j; //标记首个非0行的最后一个1的位置 
		}
		a[i][0]=len;
		a[i][1]=f1;
		if(len!=0) s2=i;//标记最后一个非全0行 
	}
	//特判 E 
	if(f+1==m){
		for(i=s1;i<=s2;i++){
			if(a[i][1]!=0) break;
		}
		if(i-1==s2){
			cout<<8;
			return 0;
		}
	}
	for(i=s1+1;i<=s2;i++){
			if(a[i-1][0]>a[i][0]){
			    f2=1;
			    if(a[i][1]==1) cout<<8;
			    else  cout<<6;
			    break;
		    }
		    else if(a[i-1][0]<a[i][0]){
			    f2=1;
			    cout<<6;
			    break;
		    }
	}
	if(f2==0) cout<<1;
	return 0;
} 
posted @ 2020-04-04 16:29  枭魈  阅读(513)  评论(0编辑  收藏  举报