[DP]筷子1/2

筷子1

时间限制: 1 Sec 内存限制: 128 MB

题目描述

A先生有很多双筷子。确切的说应该是很多根,因为筷子的长度不一,很难判断出哪两根是一双的。这天,A先生家里来了K个客人,A先生留下他们吃晚饭。加上A先生,A夫人和他们的孩子小A,共K+3个人。每人需要用一双筷子。A先生只好清理了一下筷子,共N根,长度为T1,T2,T3,……,TN.现在他想用这些筷子组合成K+3双,使每双的筷子长度差的平方和最小。(怎么不是和最小??这要去问A先生了,呵呵)

输入

有两行,第一行为两个用空格隔开的整数,表示N,K(1≤N≤100, 0

输出

仅一行。如果凑不齐K+3双,输出-1,否则输出长度差平方和的最小值。

样例输入

10 1
1 1 2 3 3 3 4 6 10 20

样例输出

5

题解

  • 读入后先对筷子长短进行排序,然后相差最小的两根一定是相邻的两根

  • dp[i,j]表示从第1根筷子到第i根筷子中配出j对最小的平方差之和

  • 状态转移方程:dp[i,j]=min(dp[i-1,j],dp[i-2,j-1]+(x[i]-x[i-1])*(x[i]-x[i-1])) (2*j<=i)
var
 t,n,s:longint;
 i,j,k,l,m:longint;
 x:array[0..100]of longint;
 dp:array[0..100,0..53]of longint;
function min(a,b:longint):longint;
begin
 if a>b
 then exit(b)
 else exit(a);
end;

procedure sort(l,r: longint);
var i,j,w,y: longint;
begin
 i:=l; j:=r; w:=x[(l+r) div 2];
 repeat
  while x[i]<w do inc(i);
  while w<x[j] do dec(j);
  if not(i>j)
  then
   begin
    y:=x[i]; x[i]:=x[j]; x[j]:=y;
    inc(i); dec(j);
   end;
 until i>j;
 if l<j then sort(l,j);
 if i<r then sort(i,r);
end;

begin
 readln(n,s); inc(s,3);
 for i:=1 to n do
  read(x[i]);
 sort(1,n);
 for i:=1 to n do
  for j:=1 to s do
   dp[i,j]:=maxlongint;
 for i:=2 to n do
  for j:=1 to s do
   if j*2<=i
   then dp[i,j]:=min(dp[i-2,j-1]+(x[i]-x[i-1])*(x[i]-x[i-1]),dp[i-1,j])
   else break;
 if dp[n,s]<>maxlongint
 then writeln(dp[n,s])
 else writeln(-1);
end.

筷子2

时间限制: 1 Sec 内存限制: 128 MB

题目描述

中国人吃饭必须要用筷子。C先生与常人不同,他的一副筷子有3只,一对再加上一根比较长的,用来穿比较大的食物。两只较短的筷子的长度应该尽可能接近,但是最长的那根的长度是无所谓的。如果一副筷子的长度分别是A,B,C(A<=B<=C),则用(A-B)2的值表示这副筷子的质量,显然这个值越小,质量越高。
C先生邀请了K个朋友去吃饭,而且他要为每个人准备一副这种特殊的筷子。C先生的家里有8个人,因此你总共必须准备K+8副筷子。不过C先生发现他的筷子有各种各样不同的长度,他必须找到一种办法搭配好K+8副筷子,使得这些筷子的质量值之和最小。

输入

第一行有一个整数T,表示文件中共有T组测试数据。T的值不超过20。每一组测试数据的第一行是两个整数K和N,(0<=K<=1000, 3K+24<=N<=5000)。K是邀请朋友的人数,N是C先生家里储备的筷子总根数。接下来的一行有N个正整数,表示这N根筷子的长度;这N个数是从小到大给出的,并且每一根筷子的长度值不超过32000。

输出

单独一行,表示最小的质量值之和。

样例输入

1
1 40
1 8 10 16 19 22 27 33 36 40 47 52 56 61 63 71 72 75 81 81 84 88 96 98 103 110 113 118 124 128 129 134 134 139 148 157 157 160 162 164

样例输出

23

题解

  • 由于要三个筷子,a,b,c要满足a<=b<=c,那么要从后(最大)向前(最小)走

  • 从前向后走会有后效性,即不能满足一定有第三根筷子大于前两根的可能,所以从后向前走

  • 状态转移方程也是倒过来就可以的,约束条件变成j*3<=n-i+1

var
 t,n,s:longint;
 i,j,k,l,m:longint;
 x:array[0..5000]of longint;
 dp:array[0..5001,0..1008]of longint;
function min(a,b:longint):longint;
begin
 if a>b
 then exit(b)
 else exit(a);
end;

begin
 readln(t);
 for l:=1 to t do
 begin
 readln(s,n); inc(s,8);
 for i:=1 to n do
  read(x[i]);
 for i:=1 to 5001 do
  for j:=1 to 1008 do
   dp[i,j]:=maxlongint;
 for i:=n downto 1 do
  for j:=1 to s do
   if j*3<=n-i+1
   then dp[i,j]:=min(dp[i+2,j-1]+(x[i]-x[i+1])*(x[i]-x[i+1]),dp[i+1,j])
   else break;
 writeln(dp[1,s]);
 end;
end.
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值