2009年11月11日

TopCoder SRM 403 Level2 1000
4和7是幸运数字,如果一个数只包含4和7,那么这个数字也是一个幸运数字
给定一个1-1,000,000的数字,求这个数字是否可以仅由幸运数字相加得出,返回所有的幸运数字加数,如果有多组解,返回加数最少的一组

以前没有遇到过这样的DP题目
这题加深了我对于“每个子问题的答案都是最佳答案”的理解
首先是额外的计算
将范围以内的幸运数字打表
然后从输入数字N开始递减DP计算
N的步数为0
如果最后0的步数为正数
说明有解
再从0开始递增寻找解的路径

不过这个方法最后也没有通过tc的系统测试
数字一大就超时了
后面又时间再研究一下
看看有没有办法在改进

 1 import java.util.*;
 2 import java.util.regex.*;
 3 import java.text.*;
 4 import java.math.*;
 5 import java.awt.geom.*;
 6 
 7 public class TheSumOfLuckyNumbers
 8 {
 9     
10     public int[] sum(int n)
11     {
12         ArrayList<Integer> table = calTable();
13         int[] dp = new int[n+1];
14         Arrays.fill(dp, Integer.MAX_VALUE-10);
15         dp[n] = 0;
16         int i , j;
17         for(i = n; i > 0 ; -- i){
18             for(int lucky:table){
19                 if(i - lucky >=0 && dp[i] + 1 < dp[i - lucky])
20                     dp[i - lucky] = dp[i] + 1;
21             }
22         }
23         if(dp[0== Integer.MAX_VALUE)
24             return new int[0];
25         ArrayList<Integer> ans = new ArrayList<Integer>();
26         int total = dp[0]-1;
27         for(i = 0 ; i <= n; ++ i){
28             for(int lucky:table){
29                 if(i + lucky <= n && dp[i + lucky] == total){
30                     ans.add(lucky);
31                     i += lucky-1;
32                     total--;
33                     break;
34                 }
35             }
36         }
37         int[] ret = new int[ans.size()];
38         for(i = 0 ; i < ans.size(); ++ i)
39             ret[i] = ans.get(i);
40         return ret;
41     }
42     
43 
44     
45     public ArrayList<Integer> calTable(){
46         ArrayList<Integer> table = new ArrayList<Integer>();
47         table.add(4);
48         table.add(7);
49         int i = 1;
50         while(i < 7){
51             ArrayList<Integer> temp = new ArrayList<Integer>();
52             for(int j = 0 ; j < table.size(); ++ j){
53                 int newItem = table.get(j) * 10;
54                 
55                 temp.add(newItem+4);
56                 temp.add(newItem+7);
57             }
58             i++;
59             table.addAll(temp);
60         }
61         return table;
62     }
63 }


posted @ 2009-12-25 16:13 jav7er 阅读(162) | 评论 (0)编辑 收藏
 
TopCoder SRM 405 Level 2 1000
一个理想的字符串,其中每个字母第一次出现的位置N,与这个字母在字符串中出现的次数M是相等的,求给定长度的字符串中最小的一个理想字符串

字符串长度小于100
相当于去搜索1~100中的一组不同的数
加起来等于长度L
同时每个数字都要小于等于比自己小的所有数的和加一
因为L比较小
所以用深搜就可以了
在这个过程中要注意深搜时候需要从大往小搜
这样才能满足“最小的”结果
搜索完之后的字符串构建的步骤因为StringBuffer用的不熟练
反而调试了很久
 1 import java.util.*;
 2 import java.util.regex.*;
 3 import java.text.*;
 4 import java.math.*;
 5 import java.awt.geom.*;
 6 
 7 public class IdealString
 8 {
 9     int[] table = new int[26];
10     int finalindex = 0;
11     int L;
12     
13     public String construct(int length)
14     {
15         Arrays.fill(table, 0);
16         L = length;
17         if(dfs(100)){
18             StringBuffer sb = new StringBuffer();
19             char c = 'A';
20             int i , j = 0;
21             for(i = 0 ; i < L; ++ i)
22                 sb.append(' ');
23             for(i = 0 ; i < finalindex; ++i){
24                 sb.setCharAt(table[i] - 1, c ++);
25                 table[i]--;
26             }
27             for(i = 0 ; i < L; ++ i){
28                 if(sb.charAt(i) != ' ')
29                     continue;
30                 while(table[j] == 0) j++;
31                 sb.setCharAt(i, (char)('A' + j));
32                 table[j]--;
33             }
34             return sb.toString();
35         }
36         return new String();
37     }
38     
39     boolean dfs(int num, int index, int sum){
40         table[index] = num;
41         int base = num + sum;
42         if(base == L){
43             table[index] = num;
44             finalindex = index + 1;
45             return true;
46         }
47         if(base > L)
48             return false;
49         int total = 0;
50         for(int i = 0 ; i <= index ; ++ i)
51             total += table[i];
52         for(int i = total + 1;  i >= num + 1-- i){            
53             if(base + i <= L && dfs(i, index +1, base))
54                 return true;
55         }
56         return false;
57     }
58 }


posted @ 2009-12-25 16:00 jav7er 阅读(137) | 评论 (0)编辑 收藏
 
TopCoder SRM 399 Level 2 1000
http://www.topcoder.com/stat?c=problem_statement&pm=8758&rd=12171
给定N和一个整数集合a,用不属于a的3个整数相乘得出离N最近的整数

N的范围1~1000
从小到大3重循环就可以解
理论上的复杂度高达1000^3
如果确实算一次我的电脑要跑到6秒
不过其实当乘积减去N已经超过之前的差额时就可以break了
所以计算量其实很小
加上break算一次只要零点零几秒
另外的陷阱是循环如果只是1~1000是不行的
有可能会用到比1000还大的因子
 1import java.util.*;
 2import java.util.regex.*;
 3import java.text.*;
 4import java.math.*;
 5import java.awt.geom.*;
 6
 7public class AvoidingProduct
 8{
 9    int SIZE = 1101;
10    
11    public int[] getTriple(int[] a, int n)
12    {
13        boolean[] table = new boolean[SIZE];
14        Arrays.fill(table, true);
15        for(int i = 0  ; i < a.length ; ++ i)
16            table[a[i]] = false;
17        int x,y,z;
18        int[] ans = new int[3];
19        Arrays.fill(ans, Integer.MAX_VALUE);
20        int gap = Integer.MAX_VALUE;
21        Outer:
22        for(x = 1 ; x < SIZE; ++ x){
23            if(table[x] == falsecontinue;
24            for(y = 1; y < SIZE; ++ y){
25                if(table[y] == falsecontinue;
26                for(z = 1 ; z < SIZE; ++ z){
27                    if(table[z] == falsecontinue;
28                    int total = x * y * z;
29                    int sub = n - total;
30                    if(Math.abs(sub) < gap){
31                        ans[0= x; ans[1= y; ans[2= z;
32                        gap = Math.abs(sub);
33                    }

34                    if(sub < 0 && Math.abs(sub) >= gap)
35                        break ;
36                }

37            }

38        }

39        return ans;
40    }

41    
42}
posted @ 2009-12-16 21:58 jav7er 阅读(123) | 评论 (0)编辑 收藏
 
TopCoder SRM 397 Level2 1000
http://www.topcoder.com/stat?c=problem_statement&pm=8746
一个彩球集合,重量各不相同,给定容器大小如何带回尽量多的彩球

限制条件中彩球个数小于13
因此用DP来计算复杂度为2^13*20*10是可以接受的
这一题用memo方法更加合适因为会有大量的数据不需要计算
每一次都迭代计算所有未填的球
把球放进口袋或者直接开始填下一袋
DP三个参数是mask,剩余袋数和本袋剩余空间
 1import java.util.*;
 2import java.util.regex.*;
 3import java.text.*;
 4import java.math.*;
 5import java.awt.geom.*;
 6
 7public class CollectingMarbles
 8{
 9    int[][][] dic;
10    int L;
11    int[] mw;
12    int bagNumber;
13    int cap;
14    
15    public int mostMarbles(int[] marblesWeights, int bagCapacity, int numberOfBags)
16    {
17        L = marblesWeights.length;
18        mw = marblesWeights;
19        bagNumber = numberOfBags;
20        cap = bagCapacity;
21        dic = new int[1<<L][bagNumber+1][cap+1];
22        for(int[][] a : dic)
23            for(int[] b : a)
24                Arrays.fill(b, -1);
25        int ans = recur(0, bagNumber, cap);
26        return ans == -1? 0 : ans;
27    }

28    
29    private int recur(int mask, int bagNumber, int cur) {
30        // TODO Auto-generated method stub
31        if(bagNumber == 0)
32            return 0;
33        if(dic[mask][bagNumber][cur] > -1)
34            return dic[mask][bagNumber][cur];
35        dic[mask][bagNumber][cur] = 0;
36        for(int i = 0 ; i < L ; ++ i){
37            if((mask & (1 << i)) == 0 && mw[i] <= cur){
38                dic[mask][bagNumber][cur] = Math.max(dic[mask][bagNumber][cur],
39                        1+recur(mask|1<<i, bagNumber, cur-mw[i]));
40                
41            }

42            dic[mask][bagNumber][cur] = Math.max(dic[mask][bagNumber][cur], 
43                    recur(mask, bagNumber-1, cap));
44        }

45        
46        return dic[mask][bagNumber][cur];
47    }

48}
posted @ 2009-12-15 15:15 jav7er 阅读(139) | 评论 (0)编辑 收藏
 
TopCoder SRM 398 Level2 900
http://www.topcoder.com/stat?c=problem_statement&pm=8160
一组字符串,如何将其中一部分右移若干格,使得某一列的纵向恰好为要求的字符串,并且给出移动最少的答案。

这一题乍一看觉得很复杂
有点像之前做过的吉他琴弦的那一题
可是这个复杂度2^50是吃不消的
但是这里有两点不同
一个是这里只能右移
另一个很重要的是这个某一列将成为一个有参考意义的坐标
即用这个列号来遍历 看答案在哪一列解答最少
想通这一点 后面就很容易了
 1import java.util.*;
 2import java.util.regex.*;
 3import java.text.*;
 4import java.math.*;
 5import java.awt.geom.*;
 6
 7public class MatchString
 8{
 9    public int placeWords(String matchString, String[] matchWords)
10    {
11        List<ArrayList<Integer> > occur = new ArrayList<ArrayList<Integer> >();
12        int L = matchWords.length;
13        int end = 0;
14        int start = Integer.MAX_VALUE;
15        int i,j;
16        for(i = 0 ; i < L ; ++ i){
17            ArrayList<Integer> temp = new ArrayList<Integer>();
18            char c = matchString.charAt(i);
19            for(j = 0 ; j < matchWords[i].length(); ++ j){
20                if(matchWords[i].charAt(j) == c)
21                    temp.add(j);
22            }

23            if(temp.isEmpty()) return -1;
24            occur.add(temp);
25            if(matchWords[i].length() > end)
26                end = matchWords[i].length();
27            if(temp.get(0< start)
28                start = temp.get(0);
29        }

30        int ans = Integer.MAX_VALUE;
31        Outer:
32        for(i = start; i <= end; ++ i){
33            int tempans = 0;
34            for(int k = 0 ; k < L; ++ k){
35                j = 0;
36                while(j < occur.get(k).size() && occur.get(k).get(j) <= i)
37                    ++j;
38                if(j == 0)
39                    continue Outer;
40                tempans += i - occur.get(k).get(j-1);                
41            }

42            ans = Math.min(ans, tempans);
43        }

44        return ans;
45    }

46}
posted @ 2009-12-15 15:09 jav7er 阅读(185) | 评论 (0)编辑 收藏
 
TopCoder SRM 396 Level2 1000
http://www.topcoder.com/stat?c=problem_statement&pm=8721&rd=12168
给定一个字符串里面包含了若干个1~9的数字,另一个字符串为其子集,对于后者中每个字符,从前面字符串中删去相应的一个数字
求删去剩下的数字的最大值

由于是求最大值,直接在计算过程中不断贪心在最高位谋取最大值
从前面按照9~1的顺序寻求可以做当前最高位的最大值
分以下4步:
1.对于所有都要删去的数字,直接删光
2.从前面开始查找,将所有无法删去的数字加入答案中
3.从前面按照9~1的顺序找可以用的最大值,如果其前面的所有数字都可以删去,那么就取这个数,否则递减
4.若字符串为空则结束,否则循环第一步

这一题足足做了2个多小时
想法很快就有了
但是在实现中String类型不能修改和删除的问题很难处理
最后干脆把String转化为LinkedList来做
不过没有看答案解决掉很难得...继续加油

 1import java.util.*;
 2import java.util.regex.*;
 3import java.text.*;
 4import java.math.*;
 5import java.awt.geom.*;
 6
 7public class RemovingDigits
 8{
 9    public String maxNumber(String number, String digits)
10    {
11        int[] numCnt = new int[10];
12        int[] digCnt = new int[10];
13        Arrays.fill(numCnt, 0);
14        Arrays.fill(digCnt, 0);
15        int i;
16        for(i = 0 ; i < number.length(); ++ i)
17            numCnt[number.charAt(i)-'0']++;
18        for(i = 0 ; i < digits.length(); ++ i)
19            digCnt[digits.charAt(i) - '0']++;
20        StringBuilder ans = new StringBuilder("");
21        List<Character> target = new LinkedList<Character>();
22        for(i = 0 ; i < number.length(); ++ i){
23            target.add(number.charAt(i));
24        }

25        while(!target.isEmpty()){
26            //remove digits that all should be removed
27            for(i = 1 ; i < 10 ; ++ i){
28                if(numCnt[i] == digCnt[i]){
29                    char c = (char) ('0'+i);
30                    while(target.remove((Character)c));
31//                    target.remove((Character)c);
32                    numCnt[i] = 0;
33                    digCnt[i] = 0;
34                }

35            }

36            //append all that cannot be removed
37            for(i = 0 ; i < target.size(); ++ i){
38                if(digCnt[target.get(i)-'0'!= 0)
39                    break;
40                ans.append(target.get(i));                    
41            }

42            for(--i;i >= 0-- i){
43                numCnt[target.get(i)-'0']--;
44                target.remove(i);
45            }

46            if(target.isEmpty())
47                break;
48                    
49            //find the largest head number
50            Outer:
51            for(i = 9 ; i > 0 ; -- i){
52                //whether there is still this number
53                if(numCnt[i] == 0)
54                    continue;
55                char c = (char) ('0'+i);
56                int idx = target.indexOf((Character)c);
57                int j;
58                int[] tempCnt = new int[10];
59                Arrays.fill(tempCnt, 0);
60                for(j = 0 ; j < idx ; ++ j)
61                    tempCnt[target.get(j)-'0']++;
62                for(j = 1 ; j < 10++ j){
63                    if(tempCnt[j] > digCnt[j])
64                        continue Outer;
65                }

66                for(j = 1; j < 10++ j){
67                    digCnt[j] -= tempCnt[j];
68                    numCnt[j] -= tempCnt[j];
69                }

70                numCnt[i]--;
71                ans.append(target.get(idx));
72                for(j = idx; j >=0--j)
73                    target.remove(j);
74                break;
75                
76            }

77        }

78        return ans.toString();
79    }

80}
posted @ 2009-11-19 20:21 jav7er 阅读(126) | 评论 (0)编辑 收藏
 
TopCoder SRM 395 Level2 1000
http://www.topcoder.com/stat?c=problem_statement&pm=8463&rd=11129
标准的DP题目

在运算中几处忽略了比较检查,特殊值
导致错误了两次
做题目在主要思路有了之后还是要仔细考虑特殊情况
往往人家关注的地方就在这里
 1import java.util.*;
 2import java.util.regex.*;
 3import java.text.*;
 4import java.math.*;
 5import java.awt.geom.*;
 6
 7public class TriviaGame
 8{
 9    public int maximumScore(int[] points, int tokensNeeded, int[] bonuses)
10    {
11        int L = points.length;
12        int[][] dic = new int[L][tokensNeeded+1];
13        for(int[] i:dic)
14            Arrays.fill(i, -100000);
15        int i;
16        dic[0][0= -1 * points[0];
17        dic[0][1= points[0];
18        if(tokensNeeded == 1)
19            dic[0][0= dic[0][1]+bonuses[0];
20        for(i = 1; i < L; ++ i){
21            if(dic[i-1][0- points[i] > dic[i][0])
22                dic[i][0= dic[i-1][0- points[i];
23            int j;
24            for(j = 1; j < Math.min(i+2, tokensNeeded); ++ j){
25                int a = dic[i-1][j-1+ points[i];
26                int b = dic[i-1][j] - points[i];
27                dic[i][j] = Math.max(a, b);                
28            }

29            if(j == tokensNeeded){
30                dic[i][j] = dic[i-1][j-1+ points[i];
31                dic[i][j] += bonuses[i];
32                if(i != L - 1 && dic[i][j] > dic[i][0])
33                    dic[i][0= dic[i][j];
34            }

35                
36        }

37        int max = dic[L-1][0];
38        for(int in:dic[L-1])
39            if(in > max)
40                max = in;
41        return max;
42    }

43}
posted @ 2009-11-18 20:24 jav7er 阅读(104) | 评论 (0)编辑 收藏
 

TopCoder SRM 394 Level2 1000
http://www.topcoder.com/stat?c=problem_statement&pm=8547&rd=11128
求a到a+b所有的数的cool divider数量之和,即本身能被整除但是其n次方不能被整除

由于数据高达10^7,暴力没有可能,将乘法转化为加法也仍然计算量太大
答案给出了很好的解决方法
对于a到a+b中间的数,可以整除i的个数为(a+b)/i - (a-1)/i,以上均为int的除法操作
这样大大节省了计算量
唯一要注意的是如果b大于a,会将一部分值本身多计算一次
因此最后要做一点修整

 

 1import java.util.*;
 2import java.util.regex.*;
 3import java.text.*;
 4import java.math.*;
 5import java.awt.geom.*;
 6
 7public class ProperDivisors
 8{
 9    public int analyzeInterval(int a, int b, int n)
10    {
11        int i,sum = 0;
12        for(i = 2 ; i <= (a+b)/2++ i)
13            sum += (a+b)/- (a-1)/i;
14        for(i = 2 ; i <= (a+b)/2++ i){
15            int k = pow(i,n);
16            sum -= (a+b)/- (a-1)/k;
17        }

18        if(b >= a){
19            sum -= (a+b)/2 - a + 1;
20            if(a == 1) sum++;
21        }

22        
23        return sum;
24    }

25    
26    public int pow(int k, int n){
27        int i;
28        long result = k;
29        for( i = 1 ; i < n; ++ i){
30            result *= k;
31            if(result > Integer.MAX_VALUE)
32                return Integer.MAX_VALUE;
33        }

34        int res = (int)result;
35        return res;
36    }

37}
posted @ 2009-11-18 20:19 jav7er 阅读(138) | 评论 (0)编辑 收藏
 

TopCoder SRM 390 Level2 1000
http://www.topcoder.com/stat?c=problem_statement&pm=8307&rd=11124
一组Pattern,每个位上是字母或者通配符“?”,求恰好符合K个Pattern的字符串数

这个题目麻烦在恰好(exactly)上
否则完全可以暴力处理
这里要恰好,即符合K个而不符合K+1个
用DP逐个位置处理
计算出每个字母匹配的Pattern集合,再和前面的Pattern集合取交集计算这一行的结果
计算过程中取交集的“&”错写成了“|” 太生疏了

 1import java.util.*;
 2import java.util.regex.*;
 3import java.text.*;
 4import java.math.*;
 5import java.awt.geom.*;
 6
 7public class SetOfPatterns
 8{
 9    public int howMany(String[] patterns, int k)
10    {
11        int[][] tab = new int[50][1<<15];
12        int L = patterns.length;
13        int T = 1 << L;
14
15        for(int[] i:tab)
16            Arrays.fill(i, 0);
17        int i,j;
18        char c;
19        for(i = 0 ; i < patterns[0].length(); ++ i){
20            
21            for(c = 'a'; c <= 'z'++ c){
22                int mask = 0 ;
23                for(j = 0 ; j < L ; ++ j){
24                    if(patterns[j].charAt(i) == c || patterns[j].charAt(i) =='?')
25                        mask |= 1 << j;
26                }

27                if(i == 0){
28                    tab[i][mask]++;
29                }

30                else{
31                    for(j = 0 ; j < (T); ++ j){
32                        int fin = j & mask;
33                        tab[i][fin] = (tab[i][fin] + tab[i-1][j])%1000003;
34                    }

35                }

36            }

37        }

38        int ans=0;
39        for(i = 0 ; i < T; ++ i){
40            int cnt = 0;
41            for(j = 0 ; j < L; ++ j){
42                int temp = i & (1 << j);
43                if(temp != 0)
44                    cnt ++;
45            }

46            if(cnt == k)
47                ans = (ans + tab[patterns[0].length()-1][i]) % 1000003;
48        }

49        return ans;
50    }

51}
posted @ 2009-11-16 22:04 jav7er 阅读(105) | 评论 (0)编辑 收藏
 
     摘要: SRM 389 Level2 1000 http://www.topcoder.com/stat?c=problem_statement&pm=8545&rd=11123 吉他有若干根弦,和弦有若干个音,按某弦的某节可以改变其音 求某和弦的最简单按发(各个弦按节最接近) 由于上限是6,数据比较小, 每个弦上需要考虑的点不超过12个 所以全部遍历也只有12^6种情况 ...  阅读全文
posted @ 2009-11-11 20:18 jav7er 阅读(113) | 评论 (0)编辑 收藏