[pku3719] Art of Balance

本文介绍了一种结合状态压缩与树形动态规划的方法,用于解决天平平衡度最小化问题。通过预处理所有可能的状态及其对应的砝码总质量,再利用树形DP找到最优解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

 

 

给出一个天平的结构,求将一些砝码放入天平的“平衡度”最小

树形的状态压缩的动态规划

预处理比较麻烦,首先枚举出所有状态,每个点的砝码数,以及每种状态对应的砝码总质量,然后进行状态压缩的树形动态规划即可

 

{
  pku3719 Art of Balance
  By largelymfs
  Accepted    17496K    469MS
  Date:2010-05-30 16:32
  状态压缩的树形动态规划
}
Program ex;
Const
  Infile = 'pku3719.in';
  Outfile=  'pku3719.out';
  maxn=16;maxs=70000;
  Inf = 9999999999999;
Var
  state : Array[0..maxn,0..maxs] Of Longint;
  count : Array[0..maxn] Of Longint;
  weight : Array[0..maxs] Of Longint;
  sum, l, r : Array[0..110] Of Longint;
  v : Array[0..maxn] of Longint;
  opt : Array[0..100, 0..maxs] Of Double;
  i, j, t, n, m : Longint;

Procedure Dfs1(num,now, k : longint);  //生成每种状态的情况
Begin
  if k>16 then Exit;
  Dfs1(num, now, k+1);
  now := now + 1 shl (k-1);
  Inc(count[num+1]);
  state[num+1][count[num+1]] := now;
  dfs1(num+1, now, k+1);
End;

Procedure Init;
Var
  ll, rr, i : Longint;
begin
  Readln(n);
  For i :=1 To n Do begin
    Readln(ll, rr);
    l[i] := ll;r[i] := rr;
  end;
  Readln(m);
  For i :=1 To m do read(v[i]);
End;

Procedure getweight;                     //得到每种状态的重量
var
  i, k : longint;
begin
  For i := 0 To (1 shl m)-1 Do begin
    weight[i] := 0;
    For k := 1 To m Do begin
      If (1 shl (k-1)) And i<>0 Then Begin
        weight[i] := weight[i] + v[k];
      end;
    end;
  end;
End;
Procedure dfs2(k : longint);          //得到每个节点下的砝码数
var
  ll, rr : longint;
Begin
  ll := l[k];rr := r[k];
  sum[k] := 0;
  If ll>0 then Begin
    dfs2(ll);sum[k] := sum[k] + sum[ll];
  end;
  If rr>0 Then begin
    dfs2(rr);sum[k] := sum[k]+sum[rr];
  end;
  If ll=rr Then Begin
    sum[k] := 1;
  end;
end;

Procedure getsum;
begin
  dfs2(1);
End;
Function get(s : Double) : Double;  //取绝对值
begin
  if s<0 Then get := -s Else get := s;
end;

procedure getdp(k, s : longint);   //进行树形动态规划
Var
  ll, i,nowl, nowr, rr : longint;
  tmp : Double;
Begin
  If opt[k][s]<0 Then begin
    opt[k][s] := Inf;
    if sum[k]=1 then begin
      opt[k][s] := 0;Exit;
    end;
    ll := l[k];rr := r[k];
    For i :=1 To count[sum[ll]] Do begin
      nowl := state[sum[ll]][i];
      if nowl and s =nowl Then Begin
        nowr := s and (not nowl);
        getdp(ll, nowl);getdp(rr, nowr);
        tmp := opt[ll][nowl]+opt[rr][nowr]+get((weight[nowl]/(weight[nowl]+weight[nowr]))-0.5)*1000;
        if tmp<opt[k][s] Then opt[k][s] := tmp;
      End;
    end;
   end;
End;

begin
  Assign(Input, Infile);Reset(Input);Assign(output,Outfile);Rewrite(Output);
  Readln(t);
  Fillchar(count, sizeof(count), 0);
  dfs1(0, 0, 1);
  While t>0 DO begin
    Dec(t);
    Init;
    getweight;
    getsum;
    For i := 1 To n do
      For j := 0 to (1 shl m)-1 Do
        opt[i][j] := -1;
    getdp(1, (1 shl m)-1);
    writeln(opt[1][(1 shl m)-1]:0:3);
  end;
  Close(Input);Close(output);
End.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值