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;
}