HihoCoder

#1043 : 完全背包 20160516

时间限制:20000ms
单点时限:1000ms
内存限制:256MB

描述

且说之前的故事里,小Hi和小Ho费劲心思终于拿到了茫茫多的奖券!而现在,终于到了小Ho领取奖励的时刻了!

等等,这段故事为何似曾相识?这就要从平行宇宙理论说起了………总而言之,在另一个宇宙中,小Ho面临的问题发生了细微的变化!

小Ho现在手上有M张奖券,而奖品区有N种奖品,分别标号为1到N,其中第i种奖品需要need(i)张奖券进行兑换,并且可以兑换无数次,为了使得辛苦得到的奖券不白白浪费,小Ho给每件奖品都评了分,其中第i件奖品的评分值为value(i),表示他对这件奖品的喜好值。现在他想知道,凭借他手上的这些奖券,可以换到哪些奖品,使得这些奖品的喜好值之和能够最大。

提示一: 切,不就是0~1变成了0~K么

提示二:强迫症患者总是会将状态转移方程优化一遍又一遍

提示三:同样不要忘了优化空间哦!

输入

每个测试点(输入文件)有且仅有一组测试数据。

每组测试数据的第一行为两个正整数N和M,表示奖品的种数,以及小Ho手中的奖券数。

接下来的n行描述每一行描述一种奖品,其中第i行为两个整数need(i)和value(i),意义如前文所述。

测试数据保证

对于100%的数据,N的值不超过500,M的值不超过10^5

对于100%的数据,need(i)不超过2*10^5, value(i)不超过10^3

输出

对于每组测试数据,输出一个整数Ans,表示小Ho可以获得的总喜好值。

样例输入
5 1000
144 990
487 436
210 673
567 58
1056 897
样例输出
5940

思路:看题目给出的提示会比较清楚。
import java.util.Scanner;

public class Main {
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        while (sc.hasNext()) {
            int n = sc.nextInt();
            int m = sc.nextInt();
            int[] need = new int[n+1];
            int[] value = new int[n+1];
            for (int i = 1; i <= n; i++) {
                need[i] = sc.nextInt();
                value[i] = sc.nextInt();
            }
            int[][] max = new int[2][m+1];
            for (int i = 1; i <= n; i++) {
                for (int j = 1; j <= m; j++) {
                    if (i % 2 == 0) {
                        if (j >= need[i]) {
                            max[0][j] = Math.max(max[0][j - need[i]] + value[i], max[1][j]);
                        } else {
                            max[0][j] = max[1][j];
                        }
                    } else {
                        if (j >= need[i]) {
                            max[1][j] = Math.max(max[1][j - need[i]] + value[i], max[0][j]);
                        } else {
                            max[1][j] = max[0][j];
                        }
                    }
                }
            }
            if (n % 2 == 0)
                System.out.println(max[0][m]);
            else
                System.out.println(max[1][m]);
        }
    }
}

 

#1038 : 01背包 20160512

时间限制:20000ms
单点时限:1000ms
内存限制:256MB

描述

且说上一周的故事里,小Hi和小Ho费劲心思终于拿到了茫茫多的奖券!而现在,终于到了小Ho领取奖励的时刻了!

小Ho现在手上有M张奖券,而奖品区有N件奖品,分别标号为1到N,其中第i件奖品需要need(i)张奖券进行兑换,同时也只能兑换一次,为了使得辛苦得到的奖券不白白浪费,小Ho给每件奖品都评了分,其中第i件奖品的评分值为value(i),表示他对这件奖品的喜好值。现在他想知道,凭借他手上的这些奖券,可以换到哪些奖品,使得这些奖品的喜好值之和能够最大。

提示一:合理抽象问题、定义状态是动态规划最关键的一步

提示二:说过了减少时间消耗,我们再来看看如何减少空间消耗

输入

每个测试点(输入文件)有且仅有一组测试数据。

每组测试数据的第一行为两个正整数N和M,表示奖品的个数,以及小Ho手中的奖券数。

接下来的n行描述每一行描述一个奖品,其中第i行为两个整数need(i)和value(i),意义如前文所述。

测试数据保证

对于100%的数据,N的值不超过500,M的值不超过10^5

对于100%的数据,need(i)不超过2*10^5, value(i)不超过10^3

输出

对于每组测试数据,输出一个整数Ans,表示小Ho可以获得的总喜好值。

样例输入
5 1000
144 990
487 436
210 673
567 58
1056 897
样例输出
2099

思路:题目提示所给思路说的非常清楚。首先得想到动态规划这种做法,因为这个问题符合动态规划的两个特征重复的子问题和无后效性(具体见提示一)。动态规划时,关键就是在第n个奖品的兑换,有兑换和不兑换两种状态(对应着01背包的1和0),兑换和不兑换的总喜好值较大值就是到第n个奖品时最大总喜好值。

也就是公式:max[i][j] = max{value[i] + max[i-1][j-need[i]], max[i-1][j]},

需要注意的就是这只在j>= need[i]时,才成立,j < need[i]时 max[i][j] = max[i-1][j]。

所以最容易想到 int[][] max = new int[n+1][m+1];然后两个循环求出max[n][m],这样一来空间复杂度就不够简化了。进一步思考发现max只有前后的依赖关系,所以只用保证前一个保存下来就能求出后一个了,所以可简化为 O(2*m)的空间复杂度。提示二中还给出了O(m)的空间复杂度,可以继续思考。

import java.util.Scanner;

public class Main {
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        while (sc.hasNext()) {
            int n = sc.nextInt();
            int m = sc.nextInt();
            int[] need = new int[n+1];
            int[] value = new int[n+1];
            for (int i = 1; i <= n; i++) {
                need[i] = sc.nextInt();
                value[i] = sc.nextInt();
            }
            //MTE n*m的数组,内存超过限制
/*            int[][] max = new int[n+1][m+1];
            for (int i = 1; i <= n; i++) {
                for (int j = 1;j <= m; j++) {
                    if (j >= need[i]) {
                        max[i][j] = Math.max(value[i] + max[i-1][j-need[i]], max[i-1][j]);
                    } else {
                        max[i][j] = max[i-1][j];
                    }                    
                }
            }
            System.out.println(max[n][m]);
            */
            //只用 2*m的空间,因为max只有前后的依赖关系,已经可以AC了
/*            int[][] max = new int[2][m+1];
            for (int i = 1; i <= n; i++) {
                for (int j = 1;j <= m; j++) {
                    if (i %2 == 0) {
                        if (j >= need[i]) {
                            max[0][j] = Math.max(value[i] + max[1][j-need[i]], max[1][j]);
                        } else {
                            max[0][j] = max[1][j];
                        }                        
                    } else {
                        if (j >= need[i]) {
                            max[1][j] = Math.max(value[i] + max[0][j-need[i]], max[0][j]);
                        } else {
                            max[1][j] = max[0][j];
                        }    
                    }
                }                
            }
            if (n % 2 == 0)
                System.out.println(max[0][m]);
            else
                System.out.println(max[1][m]);            
                */
            //只用m的空间,实在是没理解
            int[] max = new int[m+1];
            for (int i = 1; i <= n; i++) {
                for (int j = m; j >= need[i]; j--) {
                    max[j] = Math.max(max[j], max[j-need[i]]+value[i]);
                }
            }
            System.out.println(max[m]);
        }
    }
}
View Code

 思考了一下,感觉贪心算法也能实现,但是想到的复杂度会略高:原则为单位价值最大且重量最小,不超过背包最大承重量为约束条件。也就是说,存在单位重量价值相等的两个包,则选取重量较小的那个背包。

 

#1032 : 最长回文子串  20160508

时间限制:1000ms
单点时限:1000ms
内存限制:64MB

描述

   小Hi和小Ho是一对好朋友,出生在信息化社会的他们对编程产生了莫大的兴趣,他们约定好互相帮助,在编程的学习道路上一同前进。

   这一天,他们遇到了一连串的字符串,于是小Hi就向小Ho提出了那个经典的问题:“小Ho,你能不能分别在这些字符串中找到它们每一个的最长回文子串呢?”

   小Ho奇怪的问道:“什么叫做最长回文子串呢?”

   小Hi回答道:“一个字符串中连续的一段就是这个字符串的子串,而回文串指的是12421这种从前往后读和从后往前读一模一样的字符串,所以最长回文子串的意思就是这个字符串中最长的身为回文串的子串啦~”

   小Ho道:“原来如此!那么我该怎么得到这些字符串呢?我又应该怎么告诉你我所计算出的最长回文子串呢?

   小Hi笑着说道:“这个很容易啦,你只需要写一个程序,先从标准输入读取一个整数N(N<=30),代表我给你的字符串的个数,然后接下来的就是我要给你的那N个字符串(字符串长度<=10^6)啦。而你要告诉我你的答案的话,只要将你计算出的最长回文子串的长度按照我给你的顺序依次输出到标准输出就可以了!你看这就是一个例子。”

提示一 提示二 提示三 提示四
样例输入
3
abababa
aaaabaa
acacdas
样例输出
7
5
3

 个人认为这篇博客分析的比较好,虽然我是按照提示一步一步写出来的。

提交时发现必须在头尾加入两个不同的特殊字符,我下面这种方式没有在头尾加,提交we了,没想明白(详见代码中/**/注释的那一段)

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Main {
    public static void main(String[] args) throws IOException {
        InputStreamReader isr = new InputStreamReader(System.in); 
        BufferedReader br = new BufferedReader(isr);
        String s = "";
        while ((s = br.readLine()) != null) {
            int t = Integer.parseInt(s);
            for (int i = 0;i < t;i++) {
                String str = br.readLine();
                StringBuilder sb = new StringBuilder();
                sb.append("$#");
                //在原字符串每个相邻字符串中间插入'#'
                for (int j = 0;j < str.length();j++) {
                    sb.append(str.charAt(j)+"#");
                }
                sb.append("&");  
                str = sb.toString();
/*把添加特殊字符那一段修改一下就不对了,没想明白,我看出来的现象是必须在头尾加入两个不同的特殊字符,
我下面这种方式没有在头尾加,提交we了
    

//在原字符串每个相邻字符串中间插入'^'
for (int j = 0;j < str.length()-1;j++) {
sb.append(str.charAt(j)+"^");
}
sb.append(str.charAt(str.length()-1));
str = sb.toString();


*/
int ans = 1,maxj = 0,maxjk = 0,pos = 0; int[] ls = new int[str.length()]; for (int j = 0;j < str.length();j++) { //获得当前j为中心的回文子串的最小长度 int k = 0; if (2*maxj-j < 0) { k = Math.min(0, ls[maxj]-2*(j-maxj))/2; } else { k = Math.min(ls[2*maxj-j], ls[maxj]-2*(j-maxj))/2; } k = k > 0 ? k : 0; ls[j] += 2*k+1; k++; while (((j-k) >= 0) && ((j+k) < str.length())) { if (str.charAt(j-k) == str.charAt(j+k)) { ls[j] += 2; //记录以每一个字符为中心时的最大回文长度 if (j+k > maxjk) { maxj = j; //记录使右边界最大的j maxjk = j+k; //记录右边界的最大值 } k++; } else { break; } } //更新最大回文子串长度与中心字符的位置 if (ls[j] > ans) { ans = ls[j]; pos = j; } } //去掉其中的特殊字符'^' if (pos % 2 == 0) { if (ans >= 3) { ans = ans-2*((ans-3)/4+1); } } else { ans = ans-2*((ans-1)/4)-1; } System.out.println(ans); } } } }

 

 
posted @ 2016-05-08 18:31  fisherinbox  阅读(405)  评论(0编辑  收藏  举报