实验名称
优先队列式分支限界法求解0-1背包问题。
实验目的
优先队列式分支限界法求解0-1背包问题,得到不同规模数据实验的时间对比,并进行时间复杂度分析。
实验原理
使用优先队列式的分支限界算法,能准确的找出限定容量背包所能装载的商品的最大价值, 并计算出程序运行所需要的时间。
实验步骤
①根据每个物品的重量和价值计算出物品的单价,根据单价将物品进行排序;
②搜索解空间建立二叉树,从根节点开始;
③广度优先遍历二叉树,并用极大堆表示活结点的优先级,选取扩展结点,找出可行解;
④应当检查左儿子结点是否是可行结点(即上界大于当前最优值且加上该物品的重量不超过背包容量),如果是则将它加入到活结点优先队列中,而当前扩展结点的右儿子一定是可行结点,仅当右儿子满足上界约束时才将它加入到活结点优先队列中;
⑤对优先队列进行堆结构的维护,使得堆顶元素依然是优先级最高的结点;
⑥重复②③④步骤直到优先队列为空,输出结果。
时间复杂度分析
每一个物品都有放入与不放两种决策,求解上界函数的时间复杂度为O(n),因此遍历解空间树的时间复杂度为O(n^2)。
实验心得
通过这次实验,我加深了对分支界限法的学习,同时巩固了随机数据生成方法和文件读写操作的知识。
#include <iostream>
#include <stdio.h>
#include<algorithm>
#include <time.h>
#include <windows.h>
#include<fstream>
#include<queue>
using namespace std;
ifstream ifile;
ofstream ofile;
int n;
double c;
double bestp = 0.0;//最优价值best price
int bestx[1001];//最优解
struct WP{
int id;
double pre;
int v;
int w;
};
WP wp[1001];
double bound(int i,int cw,int cp)
{
double leftw= c-cw;
double b = cp;
while(i<=n && wp[i].w<=leftw)
{
leftw-=wp[i].w;
b+=wp[i].v;
i++;
}
if(i<=n)
b+=wp[i].pre*leftw;
return b;
}
bool cmp(WP a,WP b)
{
return a.pre>b.pre;
}
void init()
{
ifile>>n>>c;
for(int i=1;i<=n;i++)
{
ifile>>wp[i].w>>wp[i].v;
wp[i].pre=wp[i].v*1.0/wp[i].w*1.0;
wp[i].id=i;
}
sort(wp+1,wp+n+1,cmp);
bestp=0;
}
struct Node {
int weight;
int value;
int level;
int x[1001];
friend bool operator< (Node a, Node b)
{
return a.value < b.value;
}
};
priority_queue<Node> prique;
void enPriQueue(Node pnode, int flag)
{
Node node;
node.weight =pnode.weight;
node.value = pnode.value;
node.level = pnode.level+1;
for (int i = 0; i < node.level; i++)
node.x[i] = pnode.x[i];//把自己父节点的x转移到自己创建的新的x数组里
node.x[node.level] = flag;//自己结点的x取1还是0,通过传入的flag识别
if(flag==1){
node.value+=wp[node.level].v;
node.weight+=wp[node.level].w;
}
if (node.level == n)//判断是否是叶子结点
{
if (node.value > bestp)
{
for (int j = 1; j < n + 1; j++)
bestx[j] = node.x[j];//更新track数组,把叶子结点x数组里记录取不取的值传入track数组中
bestp = node.value;
}
return;
}
else
{
prique.push(node);//不是叶子结点就入队
}
return;
}
int prioritybbnap()
{
Node liveNode;
liveNode.weight = 0;
liveNode.value = 0;
liveNode.level = 0;
liveNode.x[0] = 0;//第一层不表示商品取舍信息
prique.push(liveNode);//把根结点入优先队列
do
{
if(bound(liveNode.level+1,liveNode.weight,liveNode.value)>bestp)
{
if (liveNode.weight + wp[liveNode.level].w <= c)//是否能进左子树
enPriQueue(liveNode,1);
enPriQueue(liveNode,0);//进入右子树
}
liveNode = prique.top();//把队列里value最大的赋值给liveNode
prique.pop();//出队--优先级为背包中价值
} while (!prique.empty());//空就停止循环
return 0;
}
int main()
{
LARGE_INTEGER frequency;
double v,beginoftime,endoftime,dt,t;
//10
ifile.close();
ifile.open("10.txt");
cout<<"10 SISE:\n";
init();
QueryPerformanceFrequency(&frequency);
v=(double)frequency.QuadPart;
QueryPerformanceCounter(&frequency);
beginoftime=frequency.QuadPart;
prioritybbnap();
QueryPerformanceCounter(&frequency);
endoftime=frequency.QuadPart;
dt=(double)(endoftime-beginoftime);
cout<<bestp;
t=dt/v;
cout<<endl<<"Time is "<<t*1000<<"ms\n";
//100
ifile.close();
ifile.open("100.txt");
cout<<"100 SISE:\n";
init();
QueryPerformanceFrequency(&frequency);
v=(double)frequency.QuadPart;
QueryPerformanceCounter(&frequency);
beginoftime=frequency.QuadPart;
prioritybbnap();
QueryPerformanceCounter(&frequency);
endoftime=frequency.QuadPart;
dt=(double)(endoftime-beginoftime);
cout<<bestp;
t=dt/v;
cout<<endl<<"Time is "<<t*1000<<"ms\n";
//1000
ifile.close();
ifile.open("1000.txt");
cout<<"1000 SISE:\n";
init();
QueryPerformanceFrequency(&frequency);
v=(double)frequency.QuadPart;
QueryPerformanceCounter(&frequency);
beginoftime=frequency.QuadPart;
prioritybbnap();
QueryPerformanceCounter(&frequency);
endoftime=frequency.QuadPart;
dt=(double)(endoftime-beginoftime);
cout<<bestp;
t=dt/v;
cout<<endl<<"Time is "<<t*1000<<"ms\n";
return 0;
}
#include <iostream>
#include <time.h>
#include <fstream>
using namespace std;//RAND_MAX=32767
int w[1000]={0};
int v[1000]={0};
int c,n;
int Size[4]={10,100,1000};//文件大小
void print(ofstream &outfile,int n,int c)//输出到文件
{
outfile<<n<<" "<<c<<endl;
for(int i=0;i<n;i++)
{
outfile<<w[i]<<' '<<v[i]<<endl;
}
}
int main()
{
int n=0;
//ifstream
ofstream out_10("10.txt"),out_100("100.txt"),out_1000("1000.txt");//输入代查找数据;
srand(time(NULL));//时间种子
//生成测试文件
for(int i=0;i<Size[0];i++)
{
w[i]=rand()%10+1;//得到[0,10)内的数
v[i]=rand()%200+1;
}
print(out_10,Size[0],20);
for(int i=0;i<Size[1];i++)
{
w[i]=rand()%10+1;
v[i]=rand()%200+1;
}
print(out_100,Size[1],20);
for(int i=0;i<Size[2];i++)
{
w[i]=rand()%10+1;
v[i]=rand()%200+1;
}
print(out_1000,Size[2],20);
}