⑴ 排队打水问题
Sample Input
3 2
1 2 3
Sample Output
7
3个人,2个水龙头,3个人打水时间分别为1、2、3,那么最少时间应该是3分钟才对,怎么会是7分钟呢?
⑵ 贪心算法:最小生成树,霍夫曼编码
连通图: 在无向图中,若任意两个顶点vi与vj都有路径相通,则称该无向图为连通图。
强连通图(Strongly Connected Graph) 是指在有向图G中,如果对于每一对vi、vj,vi≠vj,从vi到vj和从vj到vi都存在路径,则称G是强连通图。
连通网: 在连通图中,若图的边具有一定的意义,每一条边都对应着一个数,称为权;权代表着连接连个顶点的代价,称这种连通图叫做连通网。
生成树: 一个连通图的生成树是指一个连通子图,它含有图中全部n个顶点,但只有足以构成一棵树的n-1条边。一颗有n个顶点的生成树有且仅有n-1条边,如果生成树中再添加一条边,则必定成环。
最小生成树: 在连通网的所有生成树中,所有边的代价和最小的生成树,称为最小生成树。
示例:
分别使用 Kruskal算法 和 Prim算法 ,找出下图的最小生成树。
使用变长编码表对源符号(如文件中的一个字母)进行编码,其中变长编码表是通过一种评估来源符号出现机率的方法得到的,出现机率高的字母使用较短的编码,反之出现机率低的则使用较长的编码,这便使编码之后的字符串的平均长度、期望值降低,从而达到无损压缩数据的目的。
具体步骤
1.将信源符号的概率按减小的顺序排队。
2.把两个最小的概率相加,并继续这一步骤,始终将较高的概率分支放在右边,直到最后变成概率1。
3.画出由概率1处到每个信源符号的路径,顺序记下沿路径的0和1,所得就是该符号的霍夫曼码字。
4.将每对组合的左边一个指定为0,右边一个指定为1(或相反)。
示例:
假设字符a,b,c,d,e出现的概率分别为1/2,1/4,1/8,1/16,1/16。
1.求出各字符哈夫曼编码表。
2.假设一个文件有1,000,000个字符,求出编码之后文件的字节长度。
A:0
B:10
C:110
D:1110
E:1111
a所占长度l1为:(1,000,000/2) 1
b所占长度l2为:(1,000,000/4) 2
c所占长度l3为:(1,000,000/8) 3
d所占长度l4为:(1,000,000/16) 4
e所占长度l5为:(1,000,000/16)*4
文件的总长度l = l1 + l2 + l3 + l4 + l5 = 1875000
⑶ 求解一贪心算法问题
最快回答那个不懂别乱说,别误人子弟。
这题标准的贪心算法,甚至很多时候被当做贪心例题
要求平均等待时间,那么就得用 总等待时间 / 人数
所以只用关心总等待时间,
如果数据大的在前面,那么后面必然都要加一次这个时间,所以按从小到大排。
给你写了个,自己看吧。
#include "stdafx.h"
#include <iostream>
#include <algorithm>
#include <stdio.h>
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
int n;
float arr[105];
cin >> n;
for(int i = 0; i < n; ++i)
cin >> arr[i];
sort(arr, arr+n);
int tnow = 0;
int tmax = 0;
for(int i = 0; i < n; ++i)
{
tmax += tnow;
tnow += arr[i];
}
for(int i = 0; i < n; ++i)
{
printf("%0.2f ", arr[i]);
}
cout << endl;
printf("%0.2f\n",tmax / (float)n);
return 0;
}
⑷ C语言算法,用贪心法
贪心算法虽然不是最好的,但毕竟是你要求的。。。
随机取一个人,
循环开始:随机取一个没接水的人,
比较两个人的接水时间大小,让小的先接。
累加总等待时间为接水时间。
循环体结束。
输出平均接水等待时间累加T/人数n
⑸ 排队打水 pascal
type rtype=record
num,data:longint;
end;
var i,j,n:longint;
total:int64;
x:real;
a:array [1..10000] of rtype;
procere qsort(l,r:longint);
var i,j,mid:longint;
temp:rtype;
begin
i:=l; j:=r;
mid:=a[(i+j) div 2].data;
repeat
while a[i].data<mid do inc(i);
while a[j].data>mid do dec(j);
if i<=j then begin
temp:=a[i];
a[i]:=a[j];
a[j]:=temp;
inc(i);
dec(j);
end;
until i>j;
if i<r then qsort(i,r);
if l<j then qsort(l,j);
end;
begin
assign(input,'water.in');
assign(output,'water.out');
reset(input);
rewrite(output);
readln(n);
for i:=1 to n do
begin
read(a[i].data);
a[i].num:=i;
end;
readln;
qsort(1,n);
for i:=1 to n do
for j:=1 to i-1 do
total:=total+a[j].data;
x:=total/n;
for i:=1 to n do write(a[i].num,' ');
writeln;
writeln(x:0:2);
close(input);
close(output);
end.
其实很简单,就是排序,因为前面时间越少等的越少
⑹ 求贪心算法题(Pascal)
《编程之美》里面有一道买书问题的贪心算法。
题目是这样的:
在节假日的时候,书店一般都会做促销活动。由于《哈利波特》系列相当畅销,店长决定通过促销活动来回馈读者。上柜的《哈利波特》平装本系列中,一共有五卷。假设每一卷单独销售均需8欧元 。如果读者一次购买不同的两卷,就可以扣除5%的费用,三卷则更多。假设具体折扣的情况如下:
本数 折扣
2 5%
3 10%
4 20%
5 25%
在一份订单中,根据购买的卷数及本数,就会出现可以应用不同折扣规则的情况。但是,一本书只会应用一个折扣规则。比如,读者一共买了两本卷一,一本卷二。那么,可以享受到5%的折扣。另外一本卷一则不能享受折扣。如果有多种折扣,希望计算出的总额尽可能的低。
要求根据以上需求,设计出算法,能够计算出读者所购买一批书的最低价格。
⑺ 算法09-贪心算法
贪心算法与动态规划的不同在于它对每个子问题的解决方案都作出选择,不能回退。动态规划则会保存以前的运算结果,并根据以前的结果对当前进行选择,有回退功能。
很多情况下,可以在某一步用贪心算法,全局再加一个搜索或递归或动态规划之类
贪心法可以解决一些最优化问题,如:求图中的最小生成树、求哈夫曼编码等。然而对于工程和生活中的问题,贪心法一般不能得到我们所要求的答案。
一单一个问题可以通过贪心法来解决,那么贪心法一般是解决这个问题的最好办法。由于贪心法的高效性以及其所求得的答案比较接近最优结果,贪心法也可以用作辅助算法或者直接解决一些要求结果不特别精确的问题。
当硬币可选集合固定:Coins = [20,10,5,1],求最少几个硬币可以拼出总数。比如total=36。
36 - 20 = 16 20
16 - 10 = 6 20 10
6 - 5 = 1 20 10 5
1 - 1 = 0 20 10 5 1
前面这些硬币依次是后面硬币的整数倍,可以用贪心法,能得到最优解,
贪心法的反例
非整除关系的硬币,可选集合:Coins = [10,9,1],求拼出总数为18最少需要几个硬币?
最优化算法:9 + 9 = 18 两个9
贪心算法:18 - 10 = 8 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 = 0 八个1
简单地说,问题能够分解成子问题来解决,子问题的最优解能递推到最终问题的最优解。这种子问题最优解成为最优子结构。
贪心算法与动态规划的不同在于它对每个子问题的最终方案都作出选择,不能回退。
动态规划则会保存以前的运算结果,并根据以前的结果对当前进行选择,有回退功能。
假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。
对每个孩子 i,都有一个胃口值 g[i],这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j,都有一个尺寸 s[j] 。如果 s[j] >= g[i],我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。
示例 1:
输入: g = [1,2,3], s = [1,1]
输出: 1
解释:
你有三个孩子和两块小饼干,3个孩子的胃口值分别是:1,2,3。
虽然你有两块小饼干,由于他们的尺寸都是1,你只能让胃口值是1的孩子满足。
所以你应该输出1。
示例 2:
输入: g = [1,2], s = [1,2,3]
输出: 2
解释:
你有两个孩子和三块小饼干,2个孩子的胃口值分别是1,2。
你拥有的饼干数量和尺寸都足以让所有孩子满足。
所以你应该输出2.
提示:
1 <= g.length <= 3 * 104
0 <= s.length <= 3 * 104
1 <= g[i], s[j] <= 231 - 1
给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
示例 1:
输入: [7,1,5,3,6,4]
输出: 7
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。
示例 2:
输入: [1,2,3,4,5]
输出: 4
解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。
因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。
示例 3:
输入: [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。
给定一个非负整数数组 nums ,你最初位于数组的 第一个下标 。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
判断你是否能够到达最后一个下标。
示例 1:
输入:nums = [2,3,1,1,4]
输出:true
解释:可以先跳 1 步,从下标 0 到达下标 1, 然后再从下标 1 跳 3 步到达最后一个下标。
示例 2:
输入:nums = [3,2,1,0,4]
输出:false
解释:无论怎样,总会到达下标为 3 的位置。但该下标的最大跳跃长度是 0 , 所以永远不可能到达最后一个下标。
给定一个非负整数数组,你最初位于数组的第一个位置。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
你的目标是使用最少的跳跃次数到达数组的最后一个位置。
示例:
输入: [2,3,1,1,4]
输出: 2
解释: 跳到最后一个位置的最小跳跃数是 2。
从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。
说明:
假设你总是可以到达数组的最后一个位置。
移动下标只要遇到当前覆盖最远距离的下标,直接步数加一,不考虑是不是终点的情况。
想要达到这样的效果,只要让移动下标,最大只能移动到nums.size - 2的地方就可以了。
因为当移动下标指向nums.size - 2时:
如果移动下标等于当前覆盖最大距离下标, 需要再走一步(即ans++),因为最后一步一定是可以到的终点。(题目假设总是可以到达数组的最后一个位置),如图:
如果移动下标不等于当前覆盖最大距离下标,说明当前覆盖最远距离就可以直接达到终点了,不需要再走一步。如图:
机器人在一个无限大小的 XY 网格平面上行走,从点 (0, 0) 处开始出发,面向北方。该机器人可以接收以下三种类型的命令 commands :
-2 :向左转 90 度
-1 :向右转 90 度
1 <= x <= 9 :向前移动 x 个单位长度
在网格上有一些格子被视为障碍物 obstacles 。第 i 个障碍物位于网格点 obstacles[i] = (xi, yi) 。
机器人无法走到障碍物上,它将会停留在障碍物的前一个网格方块上,但仍然可以继续尝试进行该路线的其余部分。
返回从原点到机器人所有经过的路径点(坐标为整数)的最大欧式距离的平方。(即,如果距离为 5 ,则返回 25 )
注意:
北表示 +Y 方向。
东表示 +X 方向。
南表示 -Y 方向。
西表示 -X 方向。
示例 1:
输入:commands = [4,-1,3], obstacles = []
输出:25
解释:
机器人开始位于 (0, 0):
在柠檬水摊上,每一杯柠檬水的售价为 5 美元。
顾客排队购买你的产品,(按账单 bills 支付的顺序)一次购买一杯。
每位顾客只买一杯柠檬水,然后向你付 5 美元、10 美元或 20 美元。你必须给每个顾客正确找零,也就是说净交易是每位顾客向你支付 5 美元。
注意,一开始你手头没有任何零钱。
如果你能给每位顾客正确找零,返回 true ,否则返回 false 。
示例 1:
输入:[5,5,5,10,20]
输出:true
解释:
前 3 位顾客那里,我们按顺序收取 3 张 5 美元的钞票。
第 4 位顾客那里,我们收取一张 10 美元的钞票,并返还 5 美元。
第 5 位顾客那里,我们找还一张 10 美元的钞票和一张 5 美元的钞票。
由于所有客户都得到了正确的找零,所以我们输出 true。
示例 2:
输入:[5,5,10]
输出:true
示例 3:
输入:[10,10]
输出:false
示例 4:
输入:[5,5,10,10,20]
输出:false
解释:
前 2 位顾客那里,我们按顺序收取 2 张 5 美元的钞票。
对于接下来的 2 位顾客,我们收取一张 10 美元的钞票,然后返还 5 美元。
对于最后一位顾客,我们无法退回 15 美元,因为我们现在只有两张 10 美元的钞票。
由于不是每位顾客都得到了正确的找零,所以答案是 false。
给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。
你可以认为每种硬币的数量是无限的。
示例 1:
输入:coins = [1, 2, 5], amount = 11
输出:3
解释:11 = 5 + 5 + 1
示例 2:
输入:coins = [2], amount = 3
输出:-1
示例 3:
输入:coins = [1], amount = 0
输出:0
示例 4:
输入:coins = [1], amount = 1
输出:1
示例 5:
输入:coins = [1], amount = 2
输出:2
⑻ pascal题目:游戏
这题也许是贪心算法的一个简单应用 。
一开始我是这么想的:
⑼ 5. 设有n个顾客同时等待一项服务。顾客i需要的服务时间为ti,1<=i<=n。应如何安排n个顾客的服务次序才能
上面的 思路不错
最优服务次序问题
一、问题描述:
设有n 个顾客同时等待一项服务。顾客i需要的服务时间为ti, 1≦i ≦n 。共有s处可以提供此服务。应如何安排n个顾客的服务次序才能使平均等待时间达到最小?平均等待时间是n 个顾客等待服务时间的总和除以n。
二、贪心选择策略
假设原问题为T,而我们已经知道了某个最优服务系列,即最优解为A={t(1),t(2),….t(n)}(其中t(i)为第i个用户需要的服务时间),则每个用户等待时间为:
T(1)=t(1);T(2)=t(1)+t(2);...T(n):t(1)+t(2)+t(3)+……t(n);
那么总等待时问,即最优值为:
TA=n*t(1)+(n-1)*t(2)+…+(n+1-j)*t(i)+…2*t(n-1)+t(n);
由于平均等待时间是n个顾客等待时间的总和除以n,故本题实际上就是求使顾客等待时间的总和最小的服务次序。
本问题采用贪心算法求解,贪心策略如下:对服务时间最短的顾客先服务的贪心选择策略。首先对需要服务时间最短的顾客进行服务,即做完第一次选择后,原问题T变成了需对n-1个顾客服务的新问题T’。新问题和原问题相同,只是问题规模由n减小为n-1。基于此种选择策略,对新问题T’,选择n-1顾客中选择服务时间最短的先进行服务,如此进行下去,直至所有服务都完成为止 。
三、问题的贪心选择性质
先来证明该问题具有贪心选择性质,即最优服务A中t(1)满足条件:t(1)<=t(i)(2<i<n)。
用反证法来证明:假设t(1)不是最小的,不妨设t(1)>t(i)(i>1)。
设另一服务序列B=(t(i),t(2),…,t(1)…,t(n))
那么TA-TB=n*[t(1)-t(i)]+(n+1-i)[t(i)-t(1)]=(1-i)*[t(i)-t(1)]>0
即TA>TB,这与A是最优服务相矛盾。
故最优服务次序问题满足贪心选择性质。
四、问题的最优子结构性质
在进行了贪心选择后,原问题T就变成了如何安排剩余的n-1个顾客的服务次序的问题T’,是原问题的子问题。
若A是原问题T的最优解,则A’={t(2),…t(i)…t(n))是服务次序问题子问题T’的最优解。
证明:假设A’不是子问题T’的最优解,其子问题的最优解为B’,则有TB’<TA’,而根据TA的定义知,TA’十t(1)=TA。因此TB’+t(1)<TA’+t(1)=TA,即存在一个比最优值TA更短的总等待时间,而这与TA为问题T的最优值相矛盾。因此,A’是子问题T’的最优值。
从以上贪心选择及最优子结构性质的证明,可知对最优服务次序问题用贪心算法可求得最优解。
根据以上证明,最优服务次序问题可以用最短服务时间优先的贪心选择可以达到最优解。故只需对所有服务先按服务时间从小到大进行排序,然后按照排序结果依次进行服务即可。平均等待时间即为TA/n。
五、算法实现
由多处最优服务次序问题具有贪心选择性质和最优子结构性质,容易证明算法greedy的正确性。本算法采用最短服务时间优先的贪心策略。首先将每个顾客所需要的服务时间从小到大排序。然后申请2个数组:st[]是服务数组,st[j]为第j个队列上的某一个顾客的等待时间;su[]是求和数组,su[j]的值为第j个队列上所有顾客的等待时间;
double greedy(vector<int>x,int s)
{
vector<int>st(s+1,0);
vector<int>su(s+1,0);
int n=x.size();
sort(x.begin(),x.end());
int i=0,j=0;
while(i<n){
st[j]+=x[i];
su[j]+=st[j];
i++;
j++;
if(j==s)j=0;//循环分配顾客到每一个服务点上
}
double t=0;
for(i=0;i<s;i++) t+=su[i];
t/=n;
return t;
}
六、算法测试结果
七、算法复杂性分析
程序主要是花费在对各顾客所需服务时间的排序和贪心算法,即计算平均服务时间上面。其中,贪心算法部分只有一重循环影响时间复杂度,其时间复杂度为O(n):而排序算法的时间复杂度为O(nlogn)。因此,综合来看算法的时间复杂度为O(nlogn)。
八、参考文献
[1] 王晓东.计算机算法设计与分析(第3版)[M].北京:电子工业出版社,2007.
[2] 陈媛.《算法与数据结构》[M],清华大学出版社, 第1版,2005.4.
[3] 王晓东.算法设计与实验题解 [M].北京:电子工业出版社,2008.
附录(程序代码)
#include<iostream>
#include <vector>
#include<algorithm>
using namespace std;
using std::vector;
double greedy(vector<int>x,int s)
{
vector<int>st(s+1,0);
vector<int>su(s+1,0);
int n=x.size();
sort(x.begin(),x.end());
int i=0,j=0;
while(i<n){
st[j]+=x[i];
su[j]+=st[j];
i++;
j++;
if(j==s)j=0;
}
double t=0;
for(i=0;i<s;i++) t+=su[i];
t/=n;
return t;
}
void main()
{int n;//等待服务的顾客人数
int s;//服务点的个数
int i;
int a;
int t;//平均服务时间
vector<int>x;
cout<<"please input the num of the customer:"<<endl;
cin>>n;
cout<<"please input the num of the server:"<<endl;
cin>>s;
cout<<"please input the need service time of each customer:"<<endl;
for(i=1;i<=n;i++){
cout<<"No."<<i<<endl;
cin>>a;
x.push_back(a);
}
t=greedy(x, s);
cout<<"the least average waiting time is:"<<t<<endl;
}