算法设计的两个例子
标签(空格分隔): 算法知识文档
例1:调度问题
问题:
有n项任务,每项任务加工时间已知.从0时刻开始陆续安排到一台机器上加工.每个任务的完成时间是从0时刻到任务加工截至的时间.
求:
总完成时间(所有任务完成时间之和)最短的安排方案.
实例:
任务集: S={1,2,3,4,5}S={1,2,3,4,5}
加工时间: t1=3,t2=8,t3=5,t4=10,t5=15t1=3,t2=8,t3=5,t4=10,t5=15
分析:
我们需要计算的是所有任务完成时间之和.因为只有一台机器,所以做第i项任务需等待第i-1项任务做完,我们所要求解的是如何安排做这些任务的顺序使得总时间最小.比如,如果按照实例所给出的5个任务顺序的进行加工,需要的总时间计算如下:
求得: tsum=3∗5+8∗4+5∗3+10∗2+15∗1=97tsum=3∗5+8∗4+5∗3+10∗2+15∗1=97
贪心算法的解:
算法描述: 优先选择加工时间短的任务先完成
算法: 按照加工时间从小到大排列(3,5,8,10,15)(3,5,8,10,15)
解: 任务加工顺序 (1,3,2,4,5)(1,3,2,4,5)
总完成时间: tsum=3∗5+5∗4+8∗3+10∗2+15∗1=94tsum=3∗5+5∗4+8∗3+10∗2+15∗1=94
当前总时间是最小的,后面会给出证明.我们先进行问题的建模.
问题建模
输入:
任务集合: S={1,2,3,...,n}S={1,2,3,...,n}
第j项任务加工时间: tj∈Z+,j=1,2,3,..,ntj∈Z+,j=1,2,3,..,n
输出:
调度II (的排列) i1,i2,i3,...,ini1,i2,i3,...,in
目标函数:
I的完成时间, Tsum(I)=∑nk=1(n−k+1)∗tikTsum(I)=∑k=1n(n−k+1)∗tik
解:I∗I∗使得目标函数的值达到最小
证明贪心算法的正确性:
设计策略:
加工时间短任务的先做
算法:
根据加工时间从小到大排序,依次进行加工
算法的正确性:
对所有输入样例都得到最优解
证明:
加入调度ff第 i,j,j=i+1i,j,j=i+1 项任务逆序,即ti>tjti>tj
交换任务顺序j,i,j=i−1j,i,j=i−1得到新的调度gg中有tj<titj<ti
带入目标函数公式不难知道:
可知,减少一个逆序便能减少时间,将任务时间按从小到大排序得到I∗I∗,当前序列中没有相邻的逆序对,Tsum(I∗)Tsum(I∗)就是最优解.
/************************************************
@author: hejun
算法复杂度:O(NlogN)
简单的任务调度问题,还没有经过oj题目的验证
************************************************/
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define MAX_N 100005 // 最大任务总数
int t[MAX_N]; // 第i项任务需要的时间
int n; // 有n项任务
// 贪心算法解决
void solve(){
LL ans = 0;
sort(t, t+n); // 按照任务需要完成的时间从小到大排序
for(int i = 0; i < n; i++) ans += (n-i)*t[i]; // 计算最优解
printf("%lld\n", ans); // 输出最优解
}
int main(){
freopen("in.txt", "r", stdin);
int i;
while(~scanf("%d", &n) && n){ // 输入有n项任务
for(i = 0; i < n; i++) scanf("%d", &t[i]); // 输入第i项任务需要的时间
solve(); // 贪心算法解决
}
return 0;
}
运行实例:
输入:
5
3 8 5 10 15
输出:
94
例2:投资问题
问题:
mm元钱,投资个项目.效益函数fi(x)fi(x),表示第ii个项目投资元的效益, i=1,2,3,...,n.i=1,2,3,...,n.求如何分配每个项目的钱使得总效益最大?
实例:
5万元,投资给4个项目,效益函数:
xx | f2(x)f2(x) | f3(x)f3(x) | f4(x)f4(x) | |
---|---|---|---|---|
0 | 0 | 0 | 0 | 0 |
1 | 11 | 0 | 2 | 20 |
2 | 12 | 5 | 10 | 21 |
3 | 13 | 10 | 30 | 22 |
4 | 14 | 15 | 32 | 23 |
5 | 15 | 20 | 40 | 24 |
问题建模:
输入:
n,m,fi(x),i=1,2,3,..,n,x=1,2,...,mn,m,fi(x),i=1,2,3,..,n,x=1,2,...,m
解:
nn维向量
目标函数:
约束条件:
蛮力算法:
描述:
枚举所有可能的解<x1,x2,...,xn><x1,x2,...,xn>满足x1+x2+...+xn=mx1+x2+...+xn=m然后求解当前解的效益总值,得到最大的效益值对应的解向量.
实际计算:
对于当前实例:
x1+x2+x3+x4=5x1+x2+x3+x4=5
s1=<0,0,0,5>v(s1)=24s1=<0,0,0,5>v(s1)=24
s2=<0,0,1,4>v(s2)=25s2=<0,0,1,4>v(s2)=25
s3=<0,0,2,3>v(s3)=32s3=<0,0,2,3>v(s3)=32
…
s56=<5,0,0,0>v(s56)=15s56=<5,0,0,0>v(s56)=15
解:
s=<1,0,3,1>v(s)=11+0+30+20=61s=<1,0,3,1>v(s)=11+0+30+20=61
算法效率分析:
方程x1+x1+x_{2}+ … +xn=mxn=m的非负整数解<x1,x2,...,xn><x1,x2,...,xn>的个数估计:
可行解表示成0,1序列:m个1,n-1个0
1...101...101...11...101...101...1
n=4,m=7n=4,m=7
可行解<1,2,3,1><1,2,3,1>可以表示成序列:10110111011011011101
所以这样的序列个数就是在m+n−1个数字中选择m个1m+n−1个数字中选择m个1
序列个数是输入规模的指数函数:
小结:
问题求解的关键:
- 建模: 对输入参数和解给出形式化或者半形式化的描述
- 设计算法:采用什么算法设计技术,正确性–是否对所有的实例都得到正确的解
- 分析算法–效率