分礼物问题

ShoedPC 2009-12-18 08:31:54
这个题目困扰了我很久~ 纠结...
OJ的地址:
http://acm.nuaa.edu.cn/acmhome/problemdetail.do?&method=showdetail&id=1031

用它的测试数据举例。我自己的思路是,既然第一个礼物必然要被分进去,我就用100(100),500(100+400),800(100+400+300),900(100+400+300+100)... 去试着进行划分,如果哪个数把礼物划分的总数小于等于5时,这是数就是满足题目条件的最大的连续序列的总价值了。
但是程序写起来逻辑还挺复杂,好不容易写出来了,试了N次都么AC过掉。郁闷啊,是不是我的算法哪里有问题?

为了一个朋友,他说用二分解决,代码也给我了,注释也写得很详细,可是。。。。。。

import java.util.Scanner;

public class Main{

/**
* ??????,POJ3273??????????????????????????
*/
public static void main(String[] args) {
// short[] a={100, 400,300,100,500,101,400};
// int N=a.length;
// int K=4;
Scanner cin=new Scanner(System.in);
int N=cin.nextInt();
int K=cin.nextInt();
short[] a=new short[N];

int sum=0;
for(int i=0;i<N;i++){
a[i]=cin.nextShort();
sum+=a[i];
}

int left=0,right=sum;
while(left+1<right){ //????????????
int mid=(left+right)/2; //mid??????????????????
int cd=1; //??????????????????1
int leave=mid; //????????????????????????????????????????????mid
int i=0;

for(;i<N;i++){
if(mid<a[i])
break; //??????????????????mid??????????mid??????????????????????????
if(leave>=a[i]){ //????????????????
leave-=a[i];
}else{
cd++;
if(cd>K)
break; //????????????????????????????????????????????
leave=mid-a[i];
}
}

if(i==N){ //mid????????????????????????????????
right=mid;
}else{ //mid??????????????????????????????????
left=mid;

}
}
System.out.println(right);


我就纳闷了... 这可怎么办?
哪位达人能解释下这个二分的原理啊,或者能提供个其他的算法,能搞定这个问题的都成。
...全文
335 9 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
9 条回复
切换为时间正序
请发表友善的回复…
发表回复
lzy18lzy 2009-12-21
  • 打赏
  • 举报
回复
二分枚举!!!
绿色夹克衫 2009-12-19
  • 打赏
  • 举报
回复
这就是思维定势呀,Dp可以解的话,一般就自满自足了,最多再想想贪心,
我一般想不到用二分的,这题用二分效率应该是最高的。

[Quote=引用 7 楼 fancymouse 的回复:]
二分答案不是基本技巧么- -
[/Quote]
FancyMouse 2009-12-19
  • 打赏
  • 举报
回复
二分答案不是基本技巧么- -
绿色夹克衫 2009-12-19
  • 打赏
  • 举报
回复
又想了一下,明白是怎么二分的了,实际上是对最终输出的结果进行二分。

首先二分的下界是所有礼物中最值钱的,二分的上界是所有礼物的价值的总和,
这样二分来找到一个值,以这个值来划分,正好可以分为M个箱子,以这个值-1划分,
就必须分为m+1个箱子。

这个思路挺有意思,如果没有人说可以用二分,我肯定想不到这个方法。
绿色夹克衫 2009-12-19
  • 打赏
  • 举报
回复
这是用普通Dp的方法解的,主要有这样一个问题转化,
f(i,m) = Min(Max(item[i],f(i+1,m)), Max(item[i] + item[i+1],f(i+2,m)) ,......item[i] + item[i+1]+....item[m],0);

没有仔细看LZ和LS的程序,说一下我自己的想法吧。

这个问题可以用二分应该是建立在这样一个前提下的,Max(item[i],f(i+1,m)),前半部分是递增的,后半部分是递减的,因此可以用二分找到这个临界点。

using System;

namespace ConsoleApplication4
{
class Program
{
static int[] Items , Acc;
static int[,] Matrix;

static void Main(string[] args)
{
int heapCount = 5;
Items = new int[] { 100, 400, 300, 100, 500, 501, 400 };
Acc = new int[Items.Length + 1];
for (int i = 0; i < Acc.Length - 1; i++)
Acc[i + 1] = Acc[i] + Items[i];

Matrix = new int[Items.Length, heapCount];

Console.WriteLine(Solve(0,5));
}

static int Solve(int currentIndex, int heapCount)
{
if (heapCount == 1)
return Acc[Acc.Length - 1] - Acc[currentIndex];

if (currentIndex > Items.Length)
return int.MaxValue;

if (Matrix[currentIndex, heapCount - 1] == 0)
{
int min = int.MaxValue;
int value, minValue;

for (int i = currentIndex + 1; i < Items.Length; i++)
{
value = Solve(i,heapCount - 1);
minValue = Math.Max(Acc[i] - Acc[currentIndex], value);

if(minValue < min)
min = minValue;

if (Acc[i] - Acc[currentIndex] > value)
break;
}

Matrix[currentIndex, heapCount - 1] = min;
}

return Matrix[currentIndex, heapCount - 1];
}
}
}
ahhh90h 2009-12-18
  • 打赏
  • 举报
回复
围观
gnefuil 2009-12-18
  • 打赏
  • 举报
回复
二分连续序列的最大总价值,把这个值看做一个个盒子的容积,把礼物顺序的往里放,价值超过了就换下一个盒子,最后如果盒子的总个数小于m,则说明总价值还可以再小,否则总价值应该增大
dskit 2009-12-18
  • 打赏
  • 举报
回复
POJ3273:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

const int INF = 99999999 ;
const int size = 100100 ;

int data[size] ;
int insum ;
int inmax ;
int inn, inm ;

void input()
{
insum = 0 ; inmax = -1 ;
for( int i=1; i<=inn; i++ )
{
scanf( "%d", &data[i] ) ; insum += data[i] ;
inmax = inmax > data[i] ? inmax : data[i] ;
}
}

int test( int mid )
{
int cnt = 1 ; int tsum = 0 ;
for( int i=1; i<=inn; i++ )
{
if( tsum + data[i] > mid )
{
cnt++ ; tsum = data[i] ;
}
else
{
tsum += data[i] ;
}
}

return cnt ;
}

void process()
{
int left = inmax ; int right = insum ; int mid ;

while( left < right )
{
mid = ( (left+right)/2 ) ;

int cnt = test( mid ) ;
if( cnt > inm ) left = mid + 1 ;
else right = mid ;
}

printf( "%d\n", right ) ;
}

int main()
{
//freopen( "in.txt", "r", stdin ) ;

while( scanf( "%d %d", &inn, &inm ) != EOF )
{
input() ;

process() ;

//output() ;
}

return 0 ;
}

ShoedPC 2009-12-18
  • 打赏
  • 举报
回复
额,刚发现时POJ3273题~ 不过用二分是如何解决这个题目的啊?想不通...

33,025

社区成员

发帖
与我相关
我的任务
社区描述
数据结构与算法相关内容讨论专区
社区管理员
  • 数据结构与算法社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧