当前位置 : 主页 > 编程语言 > c语言 >

poj-1065

来源:互联网 收集:自由互联 发布时间:2023-09-03
//5340K 313MS Java import java.util.Collections; import java.util.List; import java.util.ArrayList; import java.util.Scanner; import java.util.Comparator; import java.util.Arrays; public class Main { private static final int MAX = 5000; pri
//5340K    313MS    Java 

 import java.util.Collections; 

 import java.util.List; 

 import java.util.ArrayList; 

 import java.util.Scanner; 

 import java.util.Comparator; 

 import java.util.Arrays; 


 public class Main { 

     private static final int MAX = 5000; 

     private class Stick implements Comparable{ 

         public int mLength; 

         public int mWeight; 

         public Stick(int length, int weight) { 

             mLength = length; 

             mWeight = weight; 

         } 

         public int compareTo(Object o) { 

             // int lenthCmpRes = this.mLength - ((Stick)o).mLength; 

             int lenthCmpRes = this.mLength - ((Stick)o).mLength; 

             return lenthCmpRes != 0 ? lenthCmpRes : 

                         this.mWeight - ((Stick)o).mWeight; 

         } 

     } 


     private class StickComparator implements Comparator<Stick> { 

         @Override 

             public int compare(Stick o1, Stick o2) { 

                 int lengthCmpRes = o1.mLength - o2.mLength; 

                 return lengthCmpRes != 0 ? lengthCmpRes: 

                         o1.mWeight - o2.mWeight; 

         } 

     } 


     private StickComparator mComparator; 

     private Stick[] Sticks; 

     private byte[] flag; 

     private int mCurStickInsertPos; 


     public void addStick(int length, int weight) { 

         Sticks[mCurStickInsertPos++] = new Stick(length, weight); 

     } 


     public void getMinSetupTime() { 

         Arrays.sort(Sticks, 0, mCurStickInsertPos, mComparator); 

         int setupTime = 0; 


         for (int i = 0; i < mCurStickInsertPos; i++) { 

             if (flag[i] == 0) { // if not handled yet 

                 // System.out.println(i); 

                 setupTime++; 

                 int curWeight = Sticks[i].mWeight; 

                 flag[i] = 1; 

                 for (int j = i+1; j < mCurStickInsertPos ;j++) { 

                     if (curWeight <= Sticks[j].mWeight && flag[j] == 0) { 

                         curWeight = Sticks[j].mWeight; 

                         flag[j] = 1; 

                         // System.out.println(Sticks[j].mLength + " " + Sticks[j].mWeight); 

                     } 

                 } 

             } 

         } 

         System.out.println(setupTime); 

     } 


     public Main() { 

         mCurStickInsertPos = 0; 

         flag = new byte[MAX]; 

         Sticks = new Stick[MAX]; 

         mComparator = new StickComparator(); 

     } 


     public void reset() { 

         for (int i = 0; i < MAX; i++) { 

             flag[i] = 0; 

         } 

         mCurStickInsertPos = 0; 

     } 


     public static void main(String[] args) { 

         Main solver = new Main(); 

         Scanner input = new Scanner(System.in); 

         int caseNum = input.nextInt(); 

         for (int i = 0; i < caseNum; i++) { 

             solver.reset(); 

             int stickNum = input.nextInt(); 

             for (int j = 0; j < stickNum; j++) { 

                 int length = input.nextInt(); 

                 int weight = input.nextInt(); 

                 solver.addStick(length, weight); 

             } 

             solver.getMinSetupTime(); 

         } 

     } 
}

//5340K    313MS    Java

贪心水题,不过却犯了不少乌龙。。。

首先是把题目的大小比较给看错了,原题是后面的stick的l和w都比前一个长才不用花时间setup,我给看反了,而两个例子在这种情况下答案也一样,看不出错...

然后是理解偏差,一个l1w2的stick在接着处理了一个4l5w的stick以后,l和w要按照最近处理的stick来参考,比如这时候,3l4w的stick就需要setup时间了. 一开始

给理解成只要能接着处理,l和w都按第一个stick的来取。

使用java本身也出了不少语言错误,java还是要多练练,POJ还要求class必须是"Main"

除去这些,题本身求解倒不难,

为了熟悉java的数组排序,这里没有用最快的方法,不过这道题对时间也不敏感。

思路是这样的,一开始按照l或者w来进行升序排序,得到一个有序的stick数组S(这里假设按照L升序排序)。

还要维护一个stick的flag数组,标示此stick是否被处理过了。

然后从S的第一个成员A开始,setupTime要+1, 取得该成员的L和W,那么对L来说,A后面所有stick的L都>=L(升序),因此L不用管了,

而W因为没有经过排序,因此要向后遍历,只要找到>=W的stick,就是可以不需要setupt时间的stick,同时在flag中标示此stick已经被处理了。

同时W要更新为这个stick的W,一直向后,直到遍历完S。

至此以A为开始的不需要额外setuptime的序列就完了,接下来,要找下一个还没有被处理的stick,作为新一轮没有额外setuptime序列的起始stick,

然后重复上面的过程,直到所有的stick都被处理完,

至此得到的setuptime就是最小的。

更效率的办法是每个L都维护一个自己的W升序序列(其实就是排序时,L相等的情况下,再考虑W),这样在对于某个L的stick序列,查找对于某个W能够不需要额外setup时间的stick就可以很快得到。

其实我不怎么喜欢贪心法,对于一些直观的题还好,但是对于某些题,

如何得到贪心的解以及证明贪心是最好的,这个过程其实没有一个统一的流程(和DP不一样),更偏向于一种直觉和智力上的挑战,

对于我这样的普通人来说,是很难学来的.

至于本题的贪心证明,先贴一个别人的证明:

其实这题需要两次贪心


初始思路,
最长不降子序列或者floyd求最长路,floyd肯定超时,写nlogn的最长不降子序列也非常蛋疼


于是,
第一次贪心:其实并非每次都要求一个最长的序列,然后删除,只要选出的序列符合"极大"即可。这里的极大和函数的极值是一个概念。
例如,(1,4) (4,4) (5,3) (9,4) 其中最大(最长)的一个序列为 (1,4)->(4,4)->(9,4)
极大的序列 为 (5,3)->(9,4)
证明:很简单,因为所有的木头都需要被加工,并且与加工的先后顺序无关,那么该木头一定
需要在某个序列中,而我们每次选择的序列是极大的,故每次操作都没有遗漏可加入的木头,因此总的操作数是最少的。


第二次贪心:得到一个极大的序列,直接枚举的复杂度是O(n^3),显然超时。进一步思考


考虑任意的序列:对于任意的序列(a,b)->(c,d) 如果该序列是合法的,那么必然满足
a<=c 且 b<=d ,不等式相加可得 a+c<=b+d


上面的不等式提示我们,对于任意的合法序列,必然满足长度与重量之和 是不降的


因此,如果我们将长度作为第一关键字,重量做为第二关键字进行升序排序,那么从左到右,选取所有w不降的序列,则该序列必然是极大序列。


证明:1.序列是合法的,这个正确性显然。2.序列是极大的,因为我们在1的前提下,尽可能多的选取木头


于是我们得到了最终的算法:O(nlgn+n^2)


1.先按第一关键字排序,若第一关键字相等,则按第二关键字排序
2.找出w不降,且未被标记的所有木头,将之标记
3.直到所有的木棍都被标记,否则继续执行2


自己的理解:

从直觉上想,如果我们不按照L或者W的升序顺序来进行操作,比如 不从L1(最小的L的stick群)开始,而是从L2(第2小L的stick群)开始,那样,后面不管怎么做,L1的stick群都不可能在本次没有额外的setup的前提下被处理(因为L永远>= L2 ),必然会比从L1开始额外的增加一次setup(从L2开始绝不对比从L1开始需要额外的setup time),这样基本证明了从L1开始是最好的,

然后是在从某个LN集群开始处理,在L确定情况下,当然是能够在不增加setup time的前提下处理越多的stick越好,这个证明其实是显而易见的。

这个证明还是不够规范,不是严格的数学推理,贪心相对于DP的优势就在于从上到下,效率高。


上一篇:cpp综合项目—机房预约系统
下一篇:没有了
网友评论