磁盘最优存储问题【深度优先搜索算法】

题目描述
设有n 个程序{1,2,…, n }要存放在长度为L的磁带上。程序i存放在磁带上的长度是Li, 1<= i<= n。这n 个程序的读取概率分别是p1,p2,...,pn,且pi+p2+...+pn = 1。如果将这n 个程序按 i1,i2,....,in 的次序存放,则读取程序ir 所需的时间tr=c*(Pi1*Li2+Pi2*Li2+...+Pir*Lir)。这n 个程序的平均读取 时间为t1+t2+...+tn。 磁带最优存储问题要求确定这n 个程序在磁带上的一个存储次序,使平均读取时间达到 最小。试设计一个解此问题的算法,并分析算法的正确性和计算复杂性。 编程任务: 对于给定的n个程序存放在磁带上的长度和读取概率,编程计算n个程序的最优存储方 案。
输入
由文件input.txt给出输入数据。第一行是正整数n,表示文件个数。接下来的n行中, 
每行有2 个正整数a 和b,分别表示程序存放在磁带上的长度和读取概率。实际上第k个程 
序的读取概率ak/(a1+a2+...+an)。对所有输入均假定c=1。
输出:
输出一个实数,保留1位小数,表示计算出的最小平均读取时间。

#include<iostream>
#include<limits.h>
#include<vector>
using namespace std;
int answernum = 0;
float answer=0;
float arr1[] = { 33,55,22,11,9 };
vector<float>v;
float smallest= INT_MAX;
void  minStorage(int n, int a[]) {
	int sum = 0;
	for (int i = 0; i < n; i++) {
		sum += arr1[a[i]];
	}
	float result = 0;
	for (int i = 0; i < n; i++) {
		for (int j = i + 1; j < n; j++) {    //从磁道0-n-1。计算它们的磁道间的检索时间
			result += (arr1[a[i]] * 1.0 / sum) * (arr1[a[j]] * 1.0 / sum) * (j - i);
		}
	}
	answernum++;
	if (result < smallest)
	{
		smallest = result;
		if (!v.empty())
			v.clear();
		for (int i = 0; i < n; i++)
			v.push_back(arr1[a[i]]);
	}
}
void swap(int arr[], int i, int j)//交换值
{
	float tmp = arr[i];
	arr[i] = arr[j];
	arr[j] = tmp;
}
float Pruning_Function(int n,int a[])
{
	int sum = 0;
	for (int i = 0; i < n; i++) {
		sum += arr1[a[i]];
	}
	float result = 0;
	for (int i = 0; i < n; i++) {
		for (int j = i + 1; j < n; j++) {    //从磁道0-n-1。计算它们的磁道间的检索时间
			result += (arr1[a[i]] * 1.0 / sum) * (arr1[a[j]] * 1.0 / sum) * (j - i);
		}
	}
	return result;
}
void func(int arr[], int i, int length)
{
	if (i == length)//跑到一个叶子节点上了
	{
		minStorage(length,arr);
	}
	else
	{
		//生成当前i节点的所有孩子节点
		for (int k = i; k < length; ++k)
		{
			swap(arr, i, k);//当前i位置的元素和后边所有元素交换
			
			if(Pruning_Function(i+1,arr)<smallest)
			func(arr, i + 1, length);//遍历i的一个孩子
		
			swap(arr, i, k);
			//回溯到父节点,一定要再交换回来,因为生成新的孩子是基于父节点进行元素的交换
		}
	}
}
int main()
{
	int arr[] = { 0,1,2,3,4};
	
	int length = sizeof(arr) / sizeof(arr[0]);
	func(arr, 0, length);
	cout << smallest<<endl;
	cout << answernum << endl;
	for (auto i = v.begin(); i != v.end(); i++)
	{
		cout << *i << " ";
	}
	return 0;
}

这个题目可以用贪心算法解,我浏览了网上的大部分代码,发现几乎都是使用贪心算法去接,但我在拿到这道题目的时候首先想到的是用深度优先搜说去遍历他,这个地方对深度优先搜索的框架不在赘述,我直接分享我设计的剪枝函数:

float Pruning_Function(int n,int a[])
{
	int sum = 0;
	for (int i = 0; i < n; i++) {
		sum += arr1[a[i]];
	}
	float result = 0;
	for (int i = 0; i < n; i++) {
		for (int j = i + 1; j < n; j++) {    //从磁道0-n-1。计算它们的磁道间的检索时间
			result += (arr1[a[i]] * 1.0 / sum) * (arr1[a[j]] * 1.0 / sum) * (j - i);
		}
	}
	return result;
}

在未剪枝前深度优先搜索一共暴力遍历了:120次

而在采用了设计的剪枝函数之后一共遍历了:5次

 

 

ChatGPT: 对于给定的n个程序存放磁带上的长度,我们可以使用贪心算法来解决这个问题。我们可以将程序按照长度从小到大排序,然后依次将程序存储磁带上,直到磁带上无法再存储下一个程序为止。这样可以保证存储程序数量最多。 证明算法的正确性:假设我们按照上述方法存储程序,但是存在一种更优的存储方案。那么这个更优的方案一定是将某个较长的程序存储在了前面,而将某个较短的程序存储在了后面。但是这样做会导致后面的磁带空间被浪费,因为后面的程序可能无法填满这个空间。因此,我们的贪心算法是正确的。 时间复杂度:排序的时间复杂度为O(nlogn),依次存储程序的时间复杂度为O(n),因此总的时间复杂度为O(nlogn)。 下面是算法的实现代码: ``` #include <iostream> #include <algorithm> using namespace std; struct Program { int id; int length; }; bool cmp(Program a, Program b) { return a.length < b.length; } int main() { int n, L; cin >> n >> L; Program p[n]; for (int i = 0; i < n; i++) { p[i].id = i + 1; cin >> p[i].length; } sort(p, p + n, cmp); int cnt = 0, sum = 0; for (int i = 0; i < n; i++) { if (sum + p[i].length <= L) { cnt++; sum += p[i].length; } else { break; } } cout << cnt << endl; return 0; } ``` 输入样例: ``` 5 10 4 3 1 2 5 ``` 输出样例: ``` 4 ``` 这个程序的输出结果是4,表示最多可以存储4个程序
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值