[SinGuLaRiTy] 贪心题目复习
Posted SinGuLaRiTy2001
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[SinGuLaRiTy] 贪心题目复习相关的知识,希望对你有一定的参考价值。
【SinGuLaRiTy-1024】 Copyright (c) SinGuLaRiTy 2017. All Rights Reserved.
[POJ 2709] 颜料 (Painter)
题目描述
杂货店出售一种由N(3<=N<=12)种不同颜色的颜料,每种一瓶(50ML),组成的颜料套装。你现在需要使用这N种颜料;不但如此,你还需要一定数量的灰色颜料。杂货店从来不出售灰色颜料——也就是它不属于这N种之一。幸运的是,灰色颜料是比较好配置的,如果你取出三种不同颜色的颜料各x ml,混合起来就可以得到x ml的灰色颜料(注意不是3x)。
现在,你知道每种颜料各需要多少ml。你决定买尽可能少的“颜料套装”,来满足你需要的这N+1种颜料。那么你最少需要买多少个套装呢?
输入
输入包含若干组测试数据。每组数据一行:第一个数N, 3<=N<=12, 含义如上;接下来N+1个数,分别表示你需要的N+1种颜料的毫升数。最后一种是灰色。所有输入的毫升数<=1000.
注意:输入中不存在每个颜料套装的毫升数。由题意可知,每种各50ml,即一共50N ml。
输出
对于每一组数据,输出一个整数,表示最少套数。
样例数据
样例输入 | 样例输出 |
3 40 95 21 0
7 25 60 400 250 0 60 0 500
4 90 95 75 95 10
4 90 95 75 95 11
5 0 0 0 0 0 333
0
|
2
8
2
3
4
|
解析
样例的前四组数据,都很简单,直接找最大,然后模拟就可以,没什么好说的。但对于第五组数据,就有点坑了。我们会发现:如果按照常规解法一次50ml地配灰色颜料,答案显然不是4.因此,在这里我们要1ml、1ml地配,直到配满灰色的为止。
Code
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=15;
int a[maxn],b[maxn];
bool cmp(int a,int b)
{
return a>b;
}
int main()
{
int n,m,k;
while(scanf("%d",&n)&&n)
{
for(int i=0;i<n;i++)
scanf("%d",&a[i]);
scanf("%d",&m);
sort(a,a+n);
if(a[n-1]%50==0) k=a[n-1]/50;
else k=a[n-1]/50+1;
int t=0;
for(int i=0;i<n;i++)
b[i]=k*50-a[i];
while(t<m)
{
if(b[2]==0)
{
k+=1;
for(int i=0;i<n;i++)
b[i]+=50;
}
else
{
t++;
b[0]--,b[1]--,b[2]--;
sort(b,b+n,cmp);
}
}
cout<<k<<endl;
}
return 0;
}
[POJ 2393] 酸奶制造厂 (Yogurt Factory)
题目描述
任务规定,一个酸奶制造厂,在N (1 <= N <= 10,000) 个星期内,分别要向外提供Y[i] (0 <= Y_i <= 10,000)单位的酸奶。已知这个制造厂第i周制造每单位酸奶的费用为C[i] (1 <= C_i <= 5,000),储存室储存每1单位酸奶1星期的费用为S (1 <= S <= 100) 。问要完成这个任务的最小费用是多少。
输入
第一行:两个整数N和S
第2~n+1行:第i+1行包含两个整数C[i]和Y[i]
输出
一个整数,表示满足安排的最小花费。注意:这个数可能会超过32位整型。
样例数据
样例输入 | 样例输出 |
4 5
88 200
89 400
97 300
91 500
|
126900
|
解析
贪心。对于每一周的订单,维护最小花费:min_cost=min(C[i],min_cost+S)。
Code
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdlib>
#include<cstdio>
#include<iostream>
#define LL long long
#define MAXN 10010
#define INF 0x3f3f3f3f
using namespace std;
LL C[MAXN];
LL Y[MAXN];
LL cost;
int main()
{
int MAX=INF;
int N,S;
scanf("%d%d",&N,&S);
for(int i=1;i<=N;i++)
{
cin>>C[i]>>Y[i];
if(C[i]>MAX+S)
C[i]=MAX+S;
MAX=C[i];
cost+=C[i]*Y[i];
}
cout<<cost;
return 0;
}
[POJ 1877] 淹没 (Flooded!)
题目描述
将一个区域分成r*c个方块,每个方块有有一个海拔(可正可负)。求当给区域注入指定容量的水时,水面的海拔是多少,以及被水淹没的方块占总方块数的百分比。每个方块的面积为100m^2,水的容量单位为立方米。
输入
多组用例,每组用例第一行为两个整数r和c表示区域行列数,然后是一个r*c的矩阵,矩阵元素为对应区域方块的海拔,最后是注入水的体积,以r=c=0结束输入
输出
对于每组用例,输出注入水后水面的海拔以及被水淹没方块占总方块数的百分比
样例数据
样例输入 | 样例输出 |
3 3
25 37 45
51 12 34
94 83 27
10000
0 0
|
Region 1
Water level is 46.67 meters.
66.67 percent of the region is under water.
|
解析
(Tips: 如果在POJ上一直莫名TLE,WA,RE,建议把默认语言的G++换成C++,玄学......)
看到这道题,首先要明白这个r*c的区域和矩阵一点关系都没有,于是果断改成一列的一维数组。此时又发现体积并不好算,于是来一个sort,此时的区域就变成了如图-1所示的样子。现在,问题就比较简单了:对于每一组数据,我们只需要判断水面从左到右延伸到了哪一个位置就能判断覆盖方块数了(在每一个位置都有一个体积范围)。而对于体积,运用小学的体积公式也就搞出来了。(注意浮点数精度误差)
图-1
Code
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
int n,m,T;
int i;
double v;
double land[1000];
int main()
{
T=1;
while(scanf("%d%d",&n,&m)!=EOF)
{
if(m==0&&n==0)
return 0;
for(i=0;i<n*m;i++)
cin>>land[i];
cin>>v;
sort(land,land+n*m);
for(i=0;i<n*m-1;i++)
{
if(v<(land[i+1]-land[i])*(i+1)*100)
break;
v-=(land[i+1]-land[i])*(i+1)*100;
}
double h=land[i]+v/(i+1)/100;
if(v==0)
h=1;
printf("Region %d\\n",T++);
printf("Water level is %.2f meters.\\n",h);
printf("%.2f percent of the region is under water.\\n",(i+1)/((double)(n*m))*100);
printf("\\n");
}
return 0;
}
[POJ 1328] 雷达安装 (Radar Installation)
题目描述
我们假设海岸是无限延伸的直线,陆地在海岸的一边,大海在另一边。每个小岛是一个点位于大海那一片区域。详见图片;
每一个雷达的安装位置只能够在海岸线上,只能够覆盖半径为 d 的圆形区域。所以,如果小岛与雷达距离小于等于d才能够覆盖。
我们可以用笛卡尔积坐标系,定义海岸线是 x轴,大海在x 上方,陆地在x 轴下方,给你每个小岛在大海中的位置,并且给出雷达的覆盖范围 d ,你的任务是写一个程序,找到最少需要安装的雷达数量且能够覆盖所有的小岛。一个位置表示(x,y)坐标。
Figure A Sample Input of Radar Installations
输入
输入是多组语句输入;
每行包含两个数 n 和 d:
n 代表小岛的数量,d 代表雷达的覆盖半径;
接下来n行是小岛的位置,用一个二维坐标来表示Xi,Yi;
当输入的 n 和 d 都为0,程序结束。
输出
对于每组数据输出一个答案,每个答案占一行,输出最小需要的雷达数量,如果不能够满足要求,则输出 -1。
样例数据
样例输入 | 样例输出 |
3 2 1 2 -3 1 2 1 1 2 0 2 0 0 |
Case 1: 2 Case 2: 1 |
解析
首先,我们将岛屿从左到右(x坐标从小到大)进行排序。接下来,我们走一遍循环,计算出能覆盖第i个岛屿的雷达的坐标范围 [ll[1],rr[1]] (相当于是x轴上的线段) ,如图-2。接下来,我们从左到右再走一次循环,这一次,我们看这些一段段线段有多少重合区域(这意味着有些岛屿可共用一个雷达),最后统计,求出答案。
图-2
Code
#include<iostream> #include<cstring> #include<cmath> #include<cstdio> using namespace std; double x[10000],y[10000],ll[11000],rr[10000]; int main() { int n,d; int cnt=1; while(cin>>n>>d) { if(n==0&&d==0) break; int flag=1,sum=1; for(int i=0;i<n;i++) { cin>>x[i]>>y[i]; if(y[i]>d) flag=0; } cout<<"Case "<<cnt; cnt++; if(flag==0||d==0) { cout<<": -1"<<endl; continue; } int t; for(int i=0;i<n-1;i++) { for(int j=0;j<n-i-1;j++) { if(x[j]>x[j+1]) { t=x[j]; x[j]=x[j+1]; x[j+1]=t; t=y[j]; y[j]=y[j+1]; y[j+1]=t; } } } for (int i=0;i<n;i++) { double s=sqrt(d*d-y[i]*y[i]); ll[i]=x[i]-s; rr[i]=x[i]+s; } double x=rr[0]; for(int i=1;i<n;i++) { if(ll[i]>x) { x=rr[i]; sum++; } if(rr[i]<x) { x=rr[i]; } } cout<<": "<<sum<<endl; } return 0; }
[POJ 1018] 通讯系统 (Communication System)
题目描述
Pizoor Communications Inc.要建立一套通信系统,该通信系统需要n种设备,而每种设备分别可以有m1、m2、m3、...、mn个厂家提供生产,而每个厂家生产的同种设备都会存在两个方面的差别:带宽bandwidths 和 价格prices。
现在每种设备都各需要1个,考虑到性价比问题,要求所挑选出来的n件设备,要使得B/P最大。其中B为这n件设备的带宽的最小值,P为这n件设备的总价。
输入
第一行包含一个整数t (1 ≤ t ≤ 10),表示有t组数据。
每组数据的第一行包含一个整数n (1 ≤ n ≤ 100),表示所需的设备种类数。接下来有n行,其中第i (1 ≤ i ≤ n)行的第一个整数mi (1 ≤ mi ≤ 100)表示生产第i种设备的厂家数量,接下来在同一行有两两成对的整数,第一个表示带宽,第二个表示价格。
输出
输出对于每一组数据而言的B/P的最大值,保留三位小数。
样例数据
样例输入 | 样例输出 |
1 3
3 100 25 150 35 80 25
2 120 80 155 40
2 100 100 120 110
|
0.649
|
解析
本来听说这道题是一道DP题,但考虑到这是贪心复习,还是用贪心+剪枝吧。
从最小带宽开始枚举,用贪心法选出带宽大于等于最小带宽的最低价格;然后再比较更新最大的(B/P)的值,直到到达最大带宽;如果从带宽枚举上去,一定符合要求。
Code
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;
const int Max=101;
struct node
{
double b;
double p;
};
node a[Max][Max];
double b[Max*Max];
int m[Max];
int bsize;
int cmp(const void *a,const void *b)
{
return (*(double *)a)-(*(double *)b);
}
int main()
{
int t,n;
cin>>t;
while (t--)
{
memset(a,0,sizeof(a));
memset(m,0,sizeof(m));
cin>>n;
int i,j,k;
int bsize=0;
for (i=0;i<n;i++)
{
cin>>m[i];
for (j=0;j<m[i];j++)
{
cin>>a[i][j].b>>a[i][j].p;
b[bsize++]=a[i][j].b;
}
}
qsort(b,bsize,sizeof(b[0]),cmp);
double mmax=0;
double mmin;
double sump=0;
double temp=0;
for (i=0;i<=bsize-1;i++)
{
sump=0;
int changFlag = 0;
for (j=0;j<n;j++)
{
mmin=32767;
for (k=0;k<m[j];k++)
{
if (a[j][k].b>=b[i]&&a[j][k].p<mmin)
{
mmin=a[j][k].p;
changFlag = 1;
}
}
sump+=mmin;
}
if(!changFlag)
break;
temp=b[i]*1.0/sump;
if(temp>mmax)
mmax=temp;
}
printf("%.3lf\\n",mmax);
}
return 0;
}
[POJ 1083] 移动桌子 (Moving Tables)
题目描述
ACM公司租用的楼层如下图所示:
这层楼的两端各有200个房间。现在公司决定在房间之间转移桌子。在移动过程中,有且仅有一张桌子能够在过道内。因此,ACM公司需要一种高效的安排来搬运桌子。已知,将一张桌子从一个房间移到另一个房间需要不到10分钟。由于将桌子从房间i到房间j移动的过程中,仅需使用过道中的i~j的部分。若两个桌子的移动不共同使用同一段过道,就可以同时进行。例如下表所示:
对于每一个房间而言,仅有一张桌子可以被移进或移出。你的任务是写一个程序来帮助计算所需的最少时间。
输入
输入包含T组数据。
对于每一组数据,第一行包含一个整数N (1 <= N <= 200),表示需要移动的桌子。接下来有N行,每行两个整数s和t,表示桌子从s移动到t。
输出
对于每一组数据,输出完成移动所需的最少时间。
样例数据
样例输入 | 样例输出 |
3
4
10 20
30 40
50 60
70 80
2
1 3
2 200
3
10 100
20 80
30 50
|
10
20
30
|
解析
只需把区间经过的地方计数,最后找到最大计数就可以了。答案的最优性说明:题意可以用区间图表示,即每一个区间为一个顶点,如果两个区间范围相交,则在其间连上一条边。题目便转化为求区间图的色数,由定理:区间图中色数=团数知道求最大团的顶点数即可。那么最大团的顶点数就是上述思路。[最大团是什么?]
Code
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define N 202
using namespace std;
int n,flag[N],T;
int main()
{
scanf("%d",&T);
while(T--)
{
int a,b,res=0;
scanf("%d",&n);
memset(flag,0,sizeof(flag));
for(int i=0;i<n;i++)
{
scanf("%d %d",&a,&b);
if(a>b)
{
swap(a,b);
}
a=(a-1)>>1;
b=(b-1)>>1;
for(int j=a;j<=b;j++)
flag[j]++;
}
for(int i=0;i<N;i++)
res=max(res,flag[i]);
printf("%d\\n",res*10);
}
return 0;
}
[HDU 1239] 再次呼叫地外文明(Calling Extraterrestrial Intelligence Again)
题目描述
给出一个大于4的整数m和一个真分数a/b,求最佳素数对p、q,使得a/b<=p/q<=1且pq<=m。最佳即为满足条件的数对中pq最大的一对。
输入
输入文件最多2000行。每一行包含了三个整数:整数m,分子a,分母b。( 4 < m <= 100000 and 1 <= a <= b <= 1000 且满足 0 < a / b < 1)
以三个0结尾。
输出
输出是一个由正整数数对组成的序列。表示每一组数据中的p、q。
样例数据
样例输入 | 样例输出 |
5 1 2
99999 999 999
1680 5 16
1970 1 1
2002 4 11
0 0 0
|
2 2
313 313
23 73
43 43
37 53
|
解析
直接枚举查找:快速求出素数表后,运用二分查找检查当前数是否为素数。(注意,由于是求最大,所以从后往前搜,直到j<z时退出)。
还要关注p的取值范围:[1,sqrt(n)]
Code
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
int prim[100005],is[100005];
int num=0;
int Find(int v)
{
int m;
int l=1,h=num;
while(l<h)
{
m=(l+h)/2;
if(prim[m]==v)
return 1;
if(prim[m]<v)
l=m+1;
else
h=m;
}
return 0;
}
int main()
{
int m,a,b,Max,j,i,x,zx,zi,z;
double t;
int s,e=(int)(sqrt(0.0+100000)+1);
memset(is,1,sizeof(is));
prim[++num]=2;
is[0]=is[1]=0;
for(i=4;i<=100000;i+=2)
is[i]=0;
for(i=3;i<e;i+=2)
if(is[i])
{
prim[++num]=i;
for(s=i*2,j=i*i;j<=100000;j+=s)
is[j]=0;
}
for(;i<100000;i+=2)
if(is[i])
prim[++num]=i;
while(scanf("%d%d%d",&m,&a,&b)!=EOF)
{
z=0;
if(m==0 && a==0 && b==0)
break;
t=a*1.0/b;
for(int j=m;j>=1;j--)
{
if(j<z)
break;
Max=(int)sqrt(0.0+j);
for(i=Max;i>=1;i--)
{
x=j/i;
if(x*t<=i && x>=i)
[SinGuLaRiTy] 分治题目复习