01背包

【题目描述】

       有一个容量为W的背包,有N种物品,第i种物品的体积为Ti,价值为Vi

       对于每种物品,你可以选择是否将它放入背包,放入背包的物品的总体积不能超过W,在这个前提下你希望放入背包的物品的总价值最大。

【输入数据】

       第一行两个数W,N,表示背包的容量和物品的数量。

       之后N行,每行两个数Ti,Vi,表示第i件物品的体积和价值。

【输出数据】

       输出一个数,放入背包的物品的最大总价值。

【样例】

bag.in

bag.out

9 3

7 14

5 9

4 7

16

【数据规模】

前边30%的数据:   n≤20    w≤100000000  3个数据;

中间40%的数据:   n≤300   w≤100000000  6个数据;

后面30%的数据:   300<n≤1000  w≤10000  3个数据。

【数据提示】

数据保证随机。

题目分析:

  题意简单,好像是动规中的01背包问题。

01背包的时间复杂度O(nV)或者O(nw),这样好像只能得30分。

怎么办呢?题目中好像只有n比较小,能不能想办法从n下手?

没有别的办法就搜吧,注意加上搜索的优化和卡时。

首先预处理数据

(1)按性价比从高到底的顺序对物品排序;

(2)贪心求出最大值;

(3)求后i项的价值总和,搜索优化用;

(4)求后i项最小的体积,搜索优化用。

搜索:

    为了防止超时,要记录搜索运行的次数,当次数<=50000000时,可以继续搜,否则果断结束,输出当前结果

var v,w,sumv,minw:array[1..1000]of int64;
    f:array[0..10000]of int64;
    m,n,i,j,ans,nowmax,tot:longint;//time,ttime:double;

procedure init;
var i:longint;
begin
  assign(input,'bag.in');reset(input);
  assign(output,'bag.out');rewrite(output);
  readln(m,n);
  for i:=1 to n do begin
    readln(w[i],v[i]);
  end;
  
end;

procedure qsort(l,r:longint);//按性价比从高到低排序
var i,j,t:longint;x:double;
begin
  i:=l;j:=r;x:=v[(l+r) shr 1]/w[(l+r) shr 1];
  repeat
    while v[i]/w[i]>x+1e-7 do inc(i);
    while v[j]/w[j]<x-1e-7 do dec(j);
    if i<=j then begin
      t:=w[i];w[i]:=w[j];w[j]:=t;t:=v[i];v[i]:=v[j];v[j]:=t;
      inc(i);dec(j);
    end;
  until i>j;
  if j>l then qsort(l,j);if i<r then qsort(i,r);
end;

function min(a,b:longint):longint;
begin if a<b then exit(a) else exit(b) end;

function max(a,b:longint):longint;
begin if a>b then exit(a) else exit(b) end;

procedure dfs_predeal;
var i,t:longint;
begin
  qsort(1,n);
  i:=0;t:=m;ans:=0;nowmax:=0;tot:=0;
  for i:=1 to n do if w[i]<=t then begin
    dec(t,w[i]);inc(ans,v[i]);
  end;//贪心法求得最大值
  for i:=n downto 1 do sumv[i]:=sumv[i+1]+v[i];{//求后i项的价值总和,搜索优化用}
  minw[n+1]:=maxlongint;
  for i:=n downto 1 do minw[i]:=min(minw[i+1],w[i]);{求后i项最小的体积,搜索优化用}
end;

procedure dfs(i,mm:longint);//考虑第i件物品,当前剩余体积为mm
begin
  if minw[i]>mm then begin//如果后i件物品中最小的体积都大于mm
    if nowmax>ans then ans:=nowmax;
    exit;
  end;
  if nowmax+sumv[i]<=ans then exit;{如果加上剩余i件物品的价值总和,还小于当前的最优值}
  inc(tot);if tot>50000000 then begin
    writeln(ans);close(output);halt;
  end;
  if mm>=w[i] then begin
    inc(nowmax,v[i]);//选第i件物品
    dfs(i+1,mm-w[i]);
    dec(nowmax,v[i]);//回溯
  end;
  dfs(i+1,mm);
end;

procedure dp;
begin
  fillchar(f,sizeof(f),0);
  for i:=1 to n do
    for j:=m downto w[i] do
      f[j]:=max(f[j],f[j-w[i]]+v[i]);
  ans:=f[m];
end;

begin
  init;
  if (m>10000) then begin
    dfs_predeal;
    dfs(1,m);
  end else dp;
  writeln(ans);
  close(output);
end.
View Code
原文地址:https://www.cnblogs.com/ssfzmfy/p/4033205.html