1319 【例6.1】排队接水
【题目描述】
有n个人在一个水龙头前排队接水,假如每个人接水的时间为Ti,请编程找出这n个人排队的一种顺序,使得n个人的平均等待时间最小。
【题目分析】
等待时间最短,节水最少的人在前
【代码实现】
#include<bits/stdc++.h>
using namespace std;
struct wtime {
int id;
int t;
};
bool cmp(wtime m, wtime n) {
return m.t < n.t;
}
wtime a[1010];
int main() {
int n, sum = 0, b = 0;
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i].t;
a[i].id = i;
}
sort(a + 1, a + 1 + n, cmp);
for (int i = 1; i < n; i++) {
b += a[i].t;
sum += b;
cout << a[i].id << " ";
}
printf("\n%.2f", sum * 1.0 / n);
return 0;
}
1320:【例6.2】均分纸牌(Noip2002)
【题目描述】
有n堆纸牌,编号分别为 1,2,…,n。每堆上有若干张,但纸牌总数必为n的倍数。可以在任一堆上取若干张纸牌,然后移动。
移牌规则为:在编号为11的堆上取的纸牌,只能移到编号为 22 的堆上;在编号为 n� 的堆上取的纸牌,只能移到编号为n−1的堆上;其他堆上取的纸牌,可以移到相邻左边或右边的堆上。
现在要求找出一种移动方法,用最少的移动次数使每堆上纸牌数都一样多。
例如 n=4,44堆纸牌数分别为: ① 9 ② 8 ③ 17 ④ 6
移动3次可达到目的:
从 ③ 取4张牌放到④(9 8 13 10
)->从③取3张牌放到 ②(9 11 10 10
)-> 从②取1张牌放到①(10 10 10 10
)。
【题目分析】
从左往右将每一堆缺少的牌或者多余的牌依次往后移
【代码实现】
#include<bits/stdc++.h>
using namespace std;
int main() {
int n, num[105], avg = 0, step = 0;
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> num[i];
avg += num[i];
}
avg /= n;
for (int i = 1; i <= n; i++) num[i] -= avg;
for (int i = 1; i <= n; i++) {
if (num[i] != 0) {
step++;
num[i + 1] += num[i];
}
}
cout << step;
return 0;
}
1321:【例6.3】删数问题(Noip1994)
【题目描述】
输入一个高精度的正整数n,去掉其中任意s个数字后剩下的数字按原左右次序组成一个新的正整数。编程对给定的n和s,寻找一种方案使得剩下的数字组成的新数最小。
输出新的正整数。(n不超过240位)
输入数据均不需判错。
【题目分析】
从左往右依次删除部分数组成一个升序,再从右往左删除部分数,按照此规则达到要求
【代码实现】
#include <bits/stdc++.h>
using namespace std;
int s;
int main() {
//input data
string str;
cin >> str >> s;
int len = str.length();
while (s--) {
int i;
for (i = 0; i < len - 1; i++) {
if (str[i] > str[i + 1]) {
// cout << str[i];
for (int j = i; j < len - 1; j++)
str[j] = str[j + 1];
break;
}
}
len--;
}
// cout << endl;
bool flag = false;
for (int i = 0; i < len; i++) {
if (str[i] != '0') flag = true;
if (flag) cout << str[i];
}
if (flag == false) cout << 0;
return 0;
}
1322:【例6.4】拦截导弹问题(Noip1999)
【题目描述】
某国为了防御敌国的导弹袭击,开发出一种导弹拦截系统,但是这种拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭,由于该系统还在试用阶段。所以一套系统有可能不能拦截所有的导弹。
输入导弹依次飞来的高度(雷达给出的高度不大于30000的正整数)。计算要拦截所有导弹最小需要配备多少套这种导弹拦截系统。
【题目分析】
使用最低的系统拦截当前的导弹
【代码实现】
#include <bits/stdc++.h>
using namespace std;
int a[1005];
int k[1005];
int main() {
//input data
int x;
int n = 0;
while (cin >> x) {
a[++n] = x;
}
int n_k = 1;
k[n_k] = a[1];
for (int i = 2; i <= n; i++) {
int temp = 30005;
int index = 0;
for (int j = 1; j <= n_k; j++) {
if (k[j] >= a[i] && temp > a[i]) {
temp = a[i];
index = j;
}
}
if (index == 0)k[++n_k] = a[i];
else k[index] = a[i];
}
cout << n_k;
return 0;
}
1323:【例6.5】活动选择
【题目描述】
学校在最近几天有n个活动,这些活动都需要使用学校的大礼堂,在同一时间,礼堂只能被一个活动使用。由于有些活动时间上有冲突,学校办公室人员只好让一些活动放弃使用礼堂而使用其他教室。
现在给出n个活动使用礼堂的起始时间begin和结束时间end(begin<end),请你帮助办公室人员安排一些活动来使用礼堂,要求安排的活动尽量多。
【题目分析】
按照结束时间排序,如果后一个的开始时间不与前面已经安排的活动结束时间冲突则进行安排
【代码实现】
#include <bits/stdc++.h>
using namespace std;
int n;
struct node {
int start;
int end;
} t[1005];
bool cmp(node &a, node &b) {
return a.end < b.end;
}
int main() {
//input data
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> t[i].start >> t[i].end;
}
sort(t + 1, t + n + 1, cmp);
int ans = 1, e = t[1].end;
for (int i = 2; i <= n; i++) {
if (t[i].start >= e) {
ans++;
e = t[i].end;
}
}
cout << ans;
return 0;
}
1324:【例6.6】整数区间
【题目描述】
请编程完成以下任务:
1.读取闭区间的个数及它们的描述;
2.找到一个含元素个数最少的集合,使得对于每一个区间,都至少有一个整数属于该集合,输出该集合的元素个数。
【题目分析】
按照区间的右端点排序,如果后一个区间的开始不与前面最大的右端点包含,则更新最大右端点,计数加一
【代码实现】
#include <bits/stdc++.h>
using namespace std;
int n;
struct node {
int start;
int end;
} t[1005];
bool cmp(node &a, node &b) {
return a.end < b.end;
}
int main() {
//input data
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> t[i].start >> t[i].end;
}
sort(t + 1, t + n + 1, cmp);
int ans = 1, e = t[1].end;
for (int i = 2; i <= n; i++) {
if (t[i].start >= e) {
ans++;
e = t[i].end;
}
}
cout << ans;
return 0;
}
1223:An Easy Problem
【题目描述】
给定一个正整数N,求最小的、比N大的正整数M,使得M与N的二进制表示中有相同数目的1。
举个例子,假如给定的N为78,其二进制表示为10011101,包含4个1,那么最小的比N大的并且二进制表示中只包含4个1的数是83,其二进制是1010011,因此83就是答案。
【题目分析】
枚举给定的整数为x,x不断的加一,直到统计的1与x相同位置
【代码实现】
#include <bits/stdc++.h>
using namespace std;
int get_one(int x) {
int sum = 0;
while (x != 0) {
if ((x & 1) == 1)sum++;
x = x >> 1;
}
return sum;
}
int main() {
//input data
int x;
while (cin >> x, x != 0) {
int sum = get_one(x);
while (true) {
x++;
if (get_one(x) == sum) break;
}
cout << x << endl;
}
return 0;
}
1224:最大子矩阵
【题目描述】
【题目分析】
【代码实现】
#include <bits/stdc++.h>
using namespace std;
const int inf = 0x3f3f3f3f;
int a[105][105];
int f[105];
int n;
int getMaxSum(int b[]) {
int maxsum = -inf;
int sum = 0;
for (int i = 1; i <= n; i++) {
if (sum > 0) sum += b[i];
else sum = b[i];
maxsum = max(maxsum, sum);
}
return maxsum;
}
int main() {
//input data
cin >> n;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
scanf("%d", &a[i][j]);
}
}
int ans = -inf;
for (int i = 1; i <= n; i++) {
memset(f, 0, sizeof(f));
for (int j = i; j <= n; j++) {
for (int k = 1; k <= n; k++) {
f[k] += a[j][k];
}
int maxf = getMaxSum(f);
ans = max(maxf, ans);
}
}
cout << ans;
return 0;
}
#include <bits/stdc++.h>
using namespace std;
int a[105][105], b[105][105];
int n;
int main() {
//input data
cin >> n;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
scanf("%d", &a[i][j]);
}
}
//求每一行从第一列开始的列和
for (int i = 1; i <= n; i++) { //行的枚举
for (int j = 2; j <= n; j++) {
a[i][j] += a[i][j - 1];
}
}
//求每一列从第一行开始的行和
for (int i = 1; i <= n; i++) {
for (int j = 2; j <= n; j++) {
a[j][i] += a[j - 1][i];
}
}
//枚举起点和终点,求子矩阵的最大和
for (int i = 1; i <= n; i++) { //终点的行号
for (int j = 1; j <= n; j++) { //终点的列号
b[i][j] = a[i][j];
for (int k = 1; k <= i; k++) { //枚举起点的行号
for (int l = 1; l <= j; l++) { //枚举起点的列号
int temp = a[i][j] - a[i][l - 1] - a[k - 1][j] + a[k - 1][l - 1];
b[i][j] = max(b[i][j], temp);
}
}
}
}
//寻找以ij为顶点的最大子矩阵
int maxa = b[1][1];
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
maxa = max(maxa, b[i][j]);
}
}
cout << maxa;
return 0;
}
1225:金银岛
【题目描述】
某天KID利用飞行器飞到了一个金银岛上,上面有许多珍贵的金属,KID虽然更喜欢各种宝石的艺术品,可是也不拒绝这样珍贵的金属。但是他只带着一个口袋,口袋至多只能装重量为w的物品。岛上金属有s个种类, 每种金属重量不同,分别为n1,n2,...,ns,同时每个种类的金属总的价值也不同,分别为v1,v2,...,vs。KID想一次带走价值尽可能多的金属,问他最多能带走价值多少的金属。注意到金属是可以被任意分割的,并且金属的价值和其重量成正比。
【题目分析】
求得所有金属的性价比,对所有的金属按照性价比进行排序,按照背包的重量的限制依次获得不同的金属
【代码实现】
#include <bits/stdc++.h>
using namespace std;
struct node {
double n;
double v;
double nv;
} a[105];
bool cmp(node &a, node &b) {
return a.nv > b.nv;
}
int main() {
//input data
int k;
cin >> k;
while (k--) {
int s, w;
cin >> w >> s;
for (int i = 1; i <= s; i++) {
cin >> a[i].n >> a[i].v;
a[i].nv = a[i].v / a[i].n;
}
sort(a + 1, a + s + 1, cmp);
double sum_w = 0;
double sum_v = 0;
for (int i = 1; i <= s; i++) {
if (sum_w + a[i].n > w) {
sum_v += (w - sum_w) * a[i].nv;
sum_w += w - sum_w;
break;
} else {
sum_w += a[i].n;
sum_v += a[i].v;
}
}
// printf("%.2lf\n", sum_w);
printf("%.2lf\n", sum_v);
}
return 0;
}
1226:装箱问题
【题目描述】
一个工厂制造的产品形状都是长方体,它们的高度都是h,长和宽都相等,一共有六个型号,他们的长宽分别为1×1,2×2,3×3,4×4,5×5,6×6。这些产品通常使用一个6×6×h的长方体包裹包装然后邮寄给客户。因为邮费很贵,所以工厂要想方设法的减小每个订单运送时的包裹数量。他们很需要有一个好的程序帮他们解决这个问题从而节省费用。现在这个程序由你来设计。
【题目分析】
从大物品到小物品进行装箱
1、6x6的物品有几个就放几个箱子
2、5x5的物品有几个就放几个箱子,剩下的空间放置1x1的物品 每箱可放11个物品
3、4x4的物品有几个就放几个箱子,剩下的空间先放2x2的物品,每箱最多放5个,再放1x1的物品
4、3x3的物品四个一箱,剩下的空间先放2x2的物品,再放1x1的物品
5、2x2的物品九个一箱,剩下的空间放置1x1的物品
6、最后剩下还要1x1的物品进行放置,36个一箱
【代码实现】
#include <bits/stdc++.h>
using namespace std;
/*
25 21 3 21 5 6
*/
int a[7];
int main() {
//input data
while (1) {
int sum = 0;
int ans = 0;
for (int i = 1; i <= 6; i++) {
cin >> a[i];
sum += a[i];
}
if (sum == 0) break;
ans += a[6]; //6x6
ans += a[5]; //5*5
if (a[1] <= a[5] * 11) {
a[1] = 0;
} else {
a[1] = a[1] - a[5] * 11;
}
ans += a[4]; //4x4
if (a[2] <= a[4] * 5) {
if (a[1] <= (a[4] * 5 - a[2]) * 4) a[1] = 0;
else a[1] = a[1] - (a[4] * 5 - a[2]) * 4;
a[2] = 0;
} else {
a[2] = a[2] - a[4] * 5;
}
ans += a[3] / 4; //3*3
if (a[3] % 4 != 0) {
ans++;
int a2 = 2 * (3 - a[3] % 4) + 1;
if (a[2] <= a2) {
int a1 = 36 - (a[3] % 4 * 9) - a[2] * 4;
if (a[1] <= a1) a[1] = 0;
else a[1] -= a1;
a[2] = 0;
} else {
a[2] -= a2;
int a1 = 36 - (a[3] % 4 * 9) - a2 * 4;
if (a[1] <= a1) a[1] = 0;
else a[1] -= a1;
}
}
ans += a[2] / 9; //2*2
if (a[2] % 9 != 0) {
ans++;
int a1 = 36 - a[2] % 9 * 4;
if (a[1] <= a1) a[1] = 0;
else a[1] -= a1;
}
ans += a[1] / 36; //1*1
if (a[1] % 36 != 0) ans++;
cout << ans << endl;
}
return 0;
}
1227:Ride to Office
【题目描述】
起点与终点相隔4500米。现Charley需要从起点骑车到终点。但是,他有个习惯,沿途需要有人陪伴,即以相同的速度,与另外一个人一起骑。而当他遇到以更快的速度骑车的人时,他会以相应的速度跟上这个更快的人。先给定所有与Charley同路的人各自的速度与出发时间,问Charley以这种方式跟人,骑完4500米需要多少时间。得出的结果若是小数,则向上取整。
【题目分析】
找到达终点最短的人
【代码实现】
#include <bits/stdc++.h>
using namespace std;
int main() {
//input data
int n;
while (cin >> n, n != 0) {
int mint = 1e9;
while (n--) {
double v, t;
cin >> v >> t;
double finishtime = 4500 / 1000.0 / v * 3600 + t;
if (t >= 0 && mint > finishtime) {
mint = ceil(finishtime);
}
}
cout << mint << endl;
}
return 0;
}
1228:书架
【题目描述】
John最近买了一个书架用来存放奶牛养殖书籍,但书架很快被存满了,只剩最顶层有空余。
John共有N头奶牛(1≤N≤20,000),每头奶牛有自己的高度Hi(1≤Hi≤10,000),N头奶牛的总高度为S。书架高度为B(1≤B≤S<2,000,000,007)。
为了到达书架顶层,奶牛可以踩着其他奶牛的背,像叠罗汉一样,直到他们的总高度不低于书架高度。当然若奶牛越多则危险性越大。为了帮助John到达书架顶层,找出使用奶牛数目最少的解决方案吧。
【题目分析】
对奶牛数据进行排序,按照降序高度依次叠加
【代码实现】
#include <bits/stdc++.h>
using namespace std;
int n, b;
int a[20005];
int main() {
//input data
cin >> n >> b;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
sort(a + 1, a + n + 1);
int i;
for (i = n; i >= 1; i--) {
b -= a[i];
if (b <= 0) break;
}
cout << n - i + 1;
return 0;
}
1229:电池的寿命
【题目描述】
小S新买了一个掌上游戏机,这个游戏机由两节5号电池供电。为了保证能够长时间玩游戏,他买了很多5号电池,这些电池的生产商不同,质量也有差异,因而使用寿命也有所不同,有的能使用5个小时,有的可能就只能使用3个小时。显然如果他只有两个电池一个能用5小时一个能用3小时,那么他只能玩3个小时的游戏,有一个电池剩下的电量无法使用,但是如果他有更多的电池,就可以更加充分地利用它们,比如他有三个电池分别能用3、3、5小时,他可以先使用两节能用3个小时的电池,使用半个小时后再把其中一个换成能使用5个小时的电池,两个半小时后再把剩下的一节电池换成刚才换下的电池(那个电池还能用2.5个小时),这样总共就可以使用5.5个小时,没有一点浪费。
现在已知电池的数量和电池能够使用的时间,请你找一种方案使得使用时间尽可能的长。
【题目分析】
除了样例中的特殊情况外,只要电池数量大于2个,都可以被用完
【代码实现】
#include <bits/stdc++.h>
using namespace std;
double a[1005];
int main() {
//input data
int n;
while (cin >> n) {
double ans = 0;
for (int i = 1; i <= n; i++) {
cin >> a[i];
ans += a[i];
}
if (n == 2 && ans >= 3 && ans <= 8) ans = 6;
printf("%.1lf\n", ans / 2);
}
return 0;
}
1230:寻找平面上的极大点
【题目描述】
在一个平面上,如果有两个点(x,y),(a,b),如果说(x,y)支配了(a,b),这是指x≥a,y≥b;
用图形来看就是(a,b)坐落在以(x,y)为右上角的一个无限的区域内。
给定n个点的集合,一定存在若干个点,它们不会被集合中的任何一点所支配,这些点叫做极大值点。
编程找出所有的极大点,按照x坐标由小到大,输出极大点的坐标。
本题规定:n不超过100,并且不考虑点的坐标为负数的情况。
【题目分析】
将所有顶点按照x坐标降序排列,x坐标相同的情况下按照y坐标降序排列,将第一个点设为极大值点,后续只要y坐标有更大的,进行maxy更新,计数加一
【代码实现】
#include <bits/stdc++.h>
using namespace std;
/*
5
1 4 2 2 3 1 2 3 1 2
*/
int n;
struct node {
int x;
int y;
} a[105], b[105];
bool cmp(node &a, node &b) {
if (a.x < b.x) return true;
if (a.x == b.x && a.y < b.y) return true;
return false;
}
int main() {
//input data
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i].x >> a[i].y;
}
sort(a + 1, a + n + 1, cmp);
//solution the question
int j = 0;
int maxy = 0;
for (int i = n; i >= 1; i--) {
if (a[i].y > maxy) {
b[++j].x = a[i].x;
b[j].y = a[i].y;
maxy = a[i].y;
}
}
//cout data
printf("(%d,%d)", b[j].x, b[j].y);
for (int i = j - 1; i >= 1; i--) {
printf(",(%d,%d)", b[i].x, b[i].y);
}
return 0;
}
1231:最小新整数
【题目描述】
给定一个十进制正整数n(0<n<1000000000),每个数位上数字均不为0。n的位数为m。
现在从m位中删除k位(0<k<m),求生成的新整数最小为多少?
例如: n=9128456,k=2,则生成的新整数最小为12456。
【题目分析】
从左往右遍历每个数,将出现降序的数字进行删除,后续数字前移一位。总长度减一,依次循环直到删除k个数字
【代码实现】
#include <bits/stdc++.h>
using namespace std;
char str[25];
int main() {
//input data
int n;
cin >> n;
while (n--) {
int k, i = 0;
char ch;
scanf("%c", &ch); //有一个回车符
while (scanf("%c", &ch), ch != ' ') str[++i] = ch;
cin >> k;
while (k--) {
for (int j = 1; j < i; j++) {
if (str[j] > str[j + 1]) {
for (int k = j; k < i; k++) {
str[k] = str[k + 1];
}
break;
}
}
i--;
}
for (int j = 1; j <= i; j++) {
printf("%c", str[j]);
}
cout << endl;
}
return 0;
}
1232:Crossing River
【题目描述】
几个人过河,每次过两人一人回,速度由慢者决定,问过河所需最短时间。
【题目分析】
对于1-10的过河者,1-10代表过河时间,过河方式有两种:
方式1:1和2过河,1回来,9和10过河,2回来 过河时间为2+1+10+2
方式2: 1和10过河,1回来,1和9过河,1回来,过河时间为10+1+9+1
9和10过河的最短时间为方式1和方式2的较短时间
当较慢者都过河后,剩下的人数可能为2也可能为3人
定义a[j]为第j个人的过河时间,a[1] a[2]....a[n]过河时间有快到慢
当j>3时,a[j]和a[j-1]的过河时间为: min(a[j]+a[j-1]+2*a[1],a[j]+2*a[2]+a[1])
当j==2时,过河时间为a[2]
当j==3时,过河时间为a[3]+a[1]+a[2]
【代码实现】
#include <bits/stdc++.h>
using namespace std;
int n, a[10005];
int sum = 0;
int mina = 1e9;
int f[10005];
int main() {
//input data
int k;
cin >> k;
while (k--) {
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
sort(a + 1, a + n + 1);
f[1] = a[1];
f[2] = a[2];
for (int i = 3; i <= n; i++) {
f[i] = min(f[i - 1] + a[i] + a[1], f[i - 2] + a[2] + a[1] + a[i] + a[2]);
}
cout << f[n] << endl;
}
return 0;
}
1233:接水问题
【题目描述】
学校里有一个水房,水房里一共装有m个龙头可供同学们打开水,每个龙头每秒钟的供水量相等,均为1。
现在有n名同学准备接水,他们的初始接水顺序已经确定。将这些同学按接水顺序从1到n编号,i号同学的接水量为wi。接水开始时,1到m号同学各占一个水龙头,并同时打开水龙头接水。当其中某名同学j完成其接水量要求wj后,下一名排队等候接水的同学k马上接替j同学的位置开始接水。这个换人的过程是瞬间完成的,且没有任何水的浪费。即j同学第x秒结束时完成接水,则k同学第x+1 秒立刻开始接水。 若当前接水人数n’不足m,则只有n’个龙头供水,其它m-n’个龙头关闭。
现在给出n名同学的接水量,按照上述接水规则,问所有同学都接完水需要多少秒。
【题目分析】
定义接水的数组a[n]和水龙头的数组b[k],将前k个人加入到水龙头数组,每次从k个b中寻找最小值进行接水的叠加,最后输出水龙头数组最大的数值
【代码实现】
#include<bits/stdc++.h>
using namespace std;
/*
8 4
23 71 87 32 70 93 80 76
*/
int a[10005], b[10005];
int n, k;
int main() {
cin >> n >> k;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
for (int i = 1; i <= k; i++) {
b[i] = a[i];
}
for (int i = k + 1; i <= n; i++) {
int mink = b[1], index=1;
for (int j = 2; j <= k; j++) { //找到最小值
if (mink > b[j]) index = j, mink = b[j];
}
b[index] += a[i];
}
int maxk = b[1];
for (int i = 2; i <= k; i++) {
if (maxk < b[i]) maxk = b[i];
}
cout << maxk;
return 0;
}