第二部分 基础算法 --> 第六章 贪心算法
1319:【例6.1】排队接水
时间限制: 1000 ms 内存限制: 65536 KB
【题目描述】
有n个人在一个水龙头前排队接水,假如每个人接水的时间为Ti,请编程找出这n个人排队的一种顺序,使得n个人的平均等待时间最小。
【输入】
共两行,第一行为n(1≤n≤1000);
第二行分别表示第1个人到第n个人每人的接水时间T1,T2,…,Tn,每个数据之间有1个空格。
【输出】
有两行,第一行为一种排队顺序,即1到n的一种排列;
第二行为这种排列方案下的平均等待时间(输出结果精确到小数点后两位)。
【输入样例】
10
56 12 1 99 1000 234 33 55 99 812
【输出样例】
3 2 7 8 1 4 9 6 10 5
291.90
【题解】
#include<bits/stdc++.h>
using namespace std;
const int N=1e3+10;
struct T{
int v,id;
}a[N];
bool cmp(T a, T b){
return a.v<b.v;
}
int main(){
int n; cin>>n;
for(int i=0; i<n; i++) {
cin>>a[i].v; a[i].id=i;
}
sort(a, a+n, cmp);
int sum=0;
for(int i=0; i<n; i++){
sum += a[i].v*(n-i-1);
cout<<a[i].id+1<<" ";
}
cout<<endl<<fixed<<setprecision(2)<<1.0*sum/n;
return 0;
}
1320:【例6.2】均分纸牌(Noip2002)
时间限制: 1000 ms 内存限制: 65536 KB
【题目描述】
有n堆纸牌,编号分别为 1,2,…,n。每堆上有若干张,但纸牌总数必为n的倍数。
可以在任一堆上取若干张纸牌,然后移动。
移牌规则为:在编号为1的堆上取的纸牌,只能移到编号为 2 的堆上;
在编号为 n 的堆上取的纸牌,只能移到编号为n−1的堆上;
其他堆上取的纸牌,可以移到相邻左边或右边的堆上。
现在要求找出一种移动方法,用最少的移动次数使每堆上纸牌数都一样多。
例如 n=4,4堆纸牌数分别为: ① 9 ② 8 ③ 17 ④ 6
移动3次可达到目的:
从 ③ 取4张牌放到④(9 8 13 10)
->从③取3张牌放到 ②(9 11 10 10)
->从②取1张牌放到①(10 10 10 10)。
【输入】
n(n 堆纸牌,1≤n≤100)
a1a2…an (n 堆纸牌,每堆纸牌初始数,l≤ai≤10000)。
【输出】
所有堆均达到相等时的最少移动次数。
【输入样例】
4
9 8 17 6
【输出样例】
3
【题解】
#include<bits/stdc++.h>
using namespace std;
const int N=110;
int a[N], sum=0;
int main(){
int n; cin>>n;
for(int i=0; i<n; i++){
cin>>a[i];
sum += a[i];
}
int avg=sum/n, ans=0;
for(int i=0; i<n; i++) a[i]-=avg;
for(int i=0; i<n; i++){
if(a[i]==0) continue;
else{
a[i+1]+=a[i];
ans++;
}
}
cout<<ans;
return 0;
}
1321:【例6.3】删数问题(Noip1994)
时间限制: 1000 ms 内存限制: 65536 KB
【题目描述】
输入一个高精度的正整数n,去掉其中任意s个数字后剩下的数字按原左右次序组成一个新的正整数。
编程对给定的n和s,寻找一种方案使得剩下的数字组成的新数最小。
输出新的正整数。(n不超过240位)
输入数据均不需判错。
【输入】
n
s
【输出】最后剩下的最小数。
【输入样例】
175438
4
【输出样例】
13
【题解】
#include<bits/stdc++.h>
using namespace std;
int main(){
string s; int n; cin>>s>>n;
int len=s.length();
for(int i=1; i<=n; i++){
//find the last bit of the ascending sequence.
int id=0;
for(int j=0; j<len-1; j++){
if(s[j]>s[j+1]){
for(int k=j; k<len-1; k++) s[k]=s[k+1];
break;
}
}
--len;
}
bool flag=0;
for(int i=0; i<len; i++){
if(s[i]!='0') flag=1;
if(flag) cout<<s[i];
}
return 0;
}
1322:【例6.4】拦截导弹问题(Noip1999)
时间限制: 1000 ms 内存限制: 65536 KB
【题目描述】
某国为了防御敌国的导弹袭击,开发出一种导弹拦截系统,但是这种拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭,由于该系统还在试用阶段。所以一套系统有可能不能拦截所有的导弹。
输入导弹依次飞来的高度(雷达给出的高度不大于30000的正整数)。计算要拦截所有导弹最小需要配备多少套这种导弹拦截系统。
【输入】n颗依次飞来的高度(1≤n≤1000)。
【输出】要拦截所有导弹最小配备的系统数k。
【输入样例】
389 207 155 300 299 170 158 65
【输出样例】
2
【提示】
输入:导弹高度: 4 3 2
输出:导弹拦截系统k=1
【题解】
#include<bits/stdc++.h>
using namespace std;
const int N=1e3+10;
int a[N], vis[N], n=0, ans=0, h;
int main(){
while(~scanf("%d", &a[++n]));
for(int i=1; i<=n; i++){
if(!vis[i]){
ans++; h=a[i];
for(int j=i+1; j<=n; j++){
if(!vis[j] && h>=a[j]){
h=a[j], vis[j]=1;
}
}
}
}
cout<<ans;
return 0;
}
1323:【例6.5】活动选择
时间限制: 1000 ms 内存限制: 65536 KB
【题目描述】
学校在最近几天有n个活动,这些活动都需要使用学校的大礼堂,在同一时间,礼堂只能被一个活动使用。由于有些活动时间上有冲突,学校办公室人员只好让一些活动放弃使用礼堂而使用其他教室。
现在给出n个活动使用礼堂的起始时间begini和结束时间endi(begini<endi),请你帮助办公室人员安排一些活动来使用礼堂,要求安排的活动尽量多。
【输入】
第一行一个整数n(n≤1000);
接下来的n行,每行两个整数,第一个begini,第二个是endi(begini<endi≤32767)。
【输出】输出最多能安排的活动个数。
【输入样例】
11
3 5
1 4
12 14
8 12
0 6
8 11
6 10
5 7
3 8
5 9
2 13
【输出样例】
4
【题解】
#include<bits/stdc++.h>
using namespace std;
const int N=1e3+10;
struct T{
int s,e;
}t[N];
bool cmp(T a, T b){
return a.e<b.e;
}
int main(){
int n; cin>>n;
for(int i=0; i<n; i++){
cin>>t[i].s>>t[i].e;
}
sort(t, t+n, cmp);
int ans=1, s=t[0].e;
for(int i=0; i<n; i++){
if(s<=t[i].s){
s=t[i].e;
ans++;
}
}
cout<<ans;
return 0;
}
1223:An Easy Problem
时间限制: 1000 ms 内存限制: 65536 KB
【题目描述】
给定一个正整数N,求最小的、比N大的正整数M,使得M与N的二进制表示中有相同数目的1。
举个例子,假如给定的N为78,其二进制表示为1001110,包含4个1,那么最小的比N大的并且二进制表示中只包含4个1的数是83,其二进制是1010011,因此83就是答案。
【输入】输入若干个数n(1≤n≤1000000),输入"0"结束。
【输出】输出若干行对应的值。
【输入样例】
1 2 3 4 78 0
【输出样例】
2
4
5
8
83
【题解】
#include<bits/stdc++.h>
using namespace std;
int main(){
int n;
while(~scanf("%d", &n) && n){
int t = n, cnt = 0;
while(t){
if(t&1) cnt++;
t>>=1;
}
for(int i=n+1; ;i++){
int a = i, b = 0;
while(a){
if(a&1) b++;
a>>=1;
}
if(b==cnt) {
cout<<i<<endl;
break;
}
}
}
return 0;
}
1324:【例6.6】整数区间
时间限制: 1000 ms 内存限制: 65536 KB
【题目描述】
请编程完成以下任务:
- 读取闭区间的个数及它们的描述;
- 找到一个含元素个数最少的集合,使得对于每一个区间,都至少有一个整数属于该集合,输出该集合的元素个数。
【输入】
首行包括区间的数目n,1≤n≤10000,接下来的n行,每行包括两个整数a,b,被一空格隔开,0≤a≤b≤10000,它们是某一个区间的开始值和结束值。
【输出】
第一行集合元素的个数,对于每一个区间都至少有一个整数属于该集合,且集合所包含元素数目最少。
【输入样例】
4
3 6
2 4
0 2
4 7
【输出样例】
2
【题解】
1224:最大子矩阵
时间限制: 1000 ms 内存限制: 65536 KB
【题目描述】
已知矩阵的大小定义为矩阵中所有元素的和。
给定一个矩阵,你的任务是找到最大的非空(大小至少是1×1)子矩阵。
比如,如下4×4的矩阵
4
0 -2 -7 0
9 2 -6 2
-4 1 -4 1
-1 8 0 -2
的最大子矩阵是
9 2
-4 1
-1 8
这个子矩阵的大小是15。
【输入】
输入是一个N×N的矩阵。输入的第一行给出N(0<N<=100)。再后面的若干行中,依次(首先从左到右给出第一行的N个整数,再从左到右给出第二行的N个整数……)给出矩阵中的N2个整数,整数之间由空白字符分隔(空格或者空行)。已知矩阵中整数的范围都在 [−127,127]。
【输出】
输出最大子矩阵的大小。
【输入样例】
4
0 -2 -7 0
9 2 -6 2
-4 1 -4 1
-1 8 0 -2
【输出样例】
15
【题解】
#include<bits/stdc++.h>
using namespace std;
const int N=110;
int a[N][N],sum[N][N],n;
int main() {
cin>>n;
for(int i=1; i<=n; i++) {
for(int j=1; j<=n; j++) {
cin>>a[i][j];
sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+a[i][j];
}
}
int ans=-2e9;
for(int x1=1; x1<=n; x1++){
for(int y1=1; y1<=n; y1++){
for(int x2=x1; x2<=n; x2++){
for(int y2=y1; y2<=n; y2++){
ans=max(ans, sum[x2][y2]-sum[x1-1][y2]-sum[x2][y1-1]+sum[x1-1][y1-1]);
}
}
}
}
cout<<ans<<endl;
return 0;
}

浙公网安备 33010602011771号