程序设计:Fence Repair

问题描述:农夫约翰为了修理栅栏,要将一块很长的木板切割成N块。准备切成的木板长度为L1、L2、···、LN,未切割之前木板的长度恰好为切割后木板长度的总和。每次切割木板时,需要的开销为这块木板的长度。例如长度为21 的木板要切成长度为5、8、8的三块木板。长21的木板切成长为13和8的板时,开销为21.再将长度为13的板切成长度为5和8 的板时,开销是13.于是合计开销为34。请求出按照目标要求将木板切割完最小的开销是多少。(1<=N<=20000,0<=Li<=50000)
输入样例:
N=3,L=(8,5,8)
输出
34

思路:
我们可以建一棵二叉树来表示切割的过程:
这里写图片描述
按照题意开销计算为15+7+8+3=33,但是算不好,因为我们需要找开销关于叶节点的计算,这样方便我们设计算法写代码。分析可以发现叶节点的深度对应于为了得到对应木板所需的切割次数,开销的合计就是各叶节点的 木板长度 * 节点深度 的总和。上面的例子的开销可以计算为:3*2+4*2+5*2+1*3+2*3=33
到了这里,我们需要找到切割消耗最少的开销,于是我们需要使得越短的板深度越深(或者说最短的板和次短的板应当是兄弟节点),所以用到了贪心算法。对于最优解来说,一定存在的情况是:最短的板应当是深度最大的叶节点之一。这个叶节点的兄弟节点是一定存在的,并且由于是同一深度的叶节点,所以对应于次短的板。
假设Li按照由小到大的顺序排列好,那么L1和L2是最小的板,且他们由一个(L1+L2)长的板切割过来,将(L1和L2)放入序列中,再找该次切割前最小的板和次小的板,即在(L1+L2),L3,L4,···,Ln一共n-1块板中找,以此递归。
代码部分:

typedef long long l1;

//输入
int N,L[MAX_N];

void solve(){
 l1 ans = 0;

//递归直到计算到木板为一块为止
  while(N>1){
   //求出最短的板和次短的板
   int mii1 = 0,mii2 = 1;
   if(L[mii1]>L[mii2])  swap(mii1,mii2);

   for(int i = 2; i<N; i++){
   if(L[i]<L[mii1])
   mii2 = mii1;
   mii1 = 1;
   }
   else if(L[i]<L[mii2]){
   mii2 = i;
   }
  }
  //将两块板拼合
  int t = L[mii1] + L[mii2];
  ans += t;

  if(mii == N-1) swap(mii1,mii2);
  L[mii1] = t;
  L[mii2] = L[N-1];
  N--;
}
printf("%lld\n", ans);
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值