2009年4月15日
用了大概一个半月的时间吧,当然中间有大概一个礼拜空着来着
这是第二次做USACO了,第一次是高中的时候,那个时候的题目跟现在的都不太一样,主要是顺序,而且那个时候是用PASCAL写的
但是高中的时候没有做完,卡在了Section 5之前,其实是因为很多东西不会,数学其实也不够好,至于理解的能力,不知道现在是不是也有所提高了
其实这次做的并不是非常顺利,我不是牛人,不可能一天扫10几道题目那种,然后每到题目半个小时就搞定
前面3个Section的题目还都不算是很难,都是训练型的,都是教你算法怎么用,从Section 4开始就有比较难的题目了,尤其是DP
DP从高中开始我就没有感觉,那时候就有人跟我说,只能多做题目,也许我现在做的还不够多吧,总感觉DP是很有用很有用的东西,所以一直想学好
不管怎么样,USACO总算是磕磕碰碰的做完了,应该在NoCow和Google的帮助下终于做完了
后面大部分题目我都看了解题报告,有些算法想得出,但是不知道该怎么应用到题目之上
现在发现这点才是最重要的,算法模块谁不会写啊,都可以提前写好一个放在那里,但是问题是怎么把这个算法应用到题目上
可能这就是所谓的建模吧,把题目变形一下,然后跟我们已知的算法联系起来
还有另外一种情况,这个题目的算法不是已知的任何算法,要自己去想的,这才是真正考验一个人算法素养的时候
就像TCO里面的题目,其实很多都是这样的,很少会给你一道题目让你直接去套一个算法的
可能原来我在这方面的理解就有偏差,我总认为,你把所有的常见算法都练熟了,所有的题目都可以横扫
但是问题就是,你能不能看得出来哪道题目用哪种算法
而且,就像DP这种题目,就算你看出来了,状态转移方程你也未必写的出来
总之还是学到了很多,虽然磕磕碰碰,但是做完了100道题还是会有收获的
不知道下一个目标是什么SGU呢,还是TCO
其实之所以喜欢USACO的一个原因就是他会告诉你测试数据,你可以很方便的Debug
像OJ这种,不告诉你测试数据的,如果遇到了WA,你就要想破脑袋去想你的程序哪里错了
而往往一个人自己看自己的程序的时候,是很难发现错误的,这就会让人很郁闷,真的是非常郁闷
更何况,有些OJ真的是很贱的,用一些超级恶心的数据来钻你的空子。
不知道这样对不对,也许是考验你的细心程度吧
SGU or TCO 呢?
TEXT
Convex Hulls |
突包 |
PROB Fencing the Cows |
突包问题,直接忽略,差不多所有的计算几何我都跳过了 |
PROB Starry Night |
超级麻烦题,用Flood Fill把所有的pattern全认出来,然后判断重复,不重复就添加新的 |
PROB Musical Themes |
DP题,技巧在于如果两个序列是theme,那么相邻的两个number差相等 |
PROB Snail Trail |
DFS,没啥技巧 |
PROB Electric Fences |
刚开始以为是Divide&Conquer,后来发现跟那道题不一样,直接搜索就可以 |
PROB Wisconsin Squares |
搜索题,Testcase只有sample那一组 |
TEXT
Heuristics & Approximate Searches |
启发式搜索,没看 |
PROB Milk Measuring |
一道我看Analysis看了两天的DP,其实还是没有深刻理解 |
PROB Window Area |
可以用矩形切割过 |
PROB Network of Schools |
强连通分量题 |
PROB Big Barn |
最大子正方形,简单的DP |
PROB All Latin Squares |
搜索+剪枝 |
PROB Canada Tour |
诡异的DP算法,Analysis的那个DP倒是还可以接受,不过Nocow上的似乎需要证明,但是又没有 |
PROB Character Recognition |
这道题目都能用DP,不得不感叹DP的伟大 |
PROB Betsy's Tour |
又是搜索+剪枝 |
PROB TeleCowmunication |
先把点转化成边,然后求最小割 |
PROB Picture |
离散化 |
PROB Hidden Passwords |
搜索+KMP优化 |
PROB Two Five |
算是道难题吧,Analysis都看了好久,DP真是无所不能 |
第六个Section的就不写了,两个DP+一个牛叉的位运算
那个Cow XOR我估计我这辈子都不会忘记了。
TEXT
Optimization Techniques |
讲怎么剪枝的 |
PROB Beef McNuggets |
初看上去像是一道背包问题,但是用背包肯定超时,后来看了解题报告,发现原来是数学题 |
PROB Fence Rails |
高维背包问题,只能搜索 |
PROB Fence Loops |
其实是很简单的一道最短路问题,恶心就恶心在图的转化 |
PROB Cryptcowgraphy |
非常恶心的搜索+剪枝 |
TEXT
"Network Flow" Algorithms |
网络流,我第一次会写网络流就是看了这个算法 |
PROB Drainage Ditches |
网络流练习题 |
PROB The Perfect Stall |
最大匹配,匈牙利算法 |
PROB Job Processing |
第一问是贪心,第二问应该也还是贪心,就是把第一问最快做完的给第二问最慢做完的 |
PROB Cowcycles |
直接枚举的好像 |
TEXT Big
Numbers |
高精度 |
PROB Buy Low, Buy Lower |
经典DP,最长下降序列,可是问题是要求出现了多少次,于是我看了解题报告 |
PROB The Primes |
搜索+剪枝,要注意搜索的顺序,先是第五行第五列,然后对角线,然后其他 |
PROB Street Race |
关键路径,去掉每一个节点,然后看看起点与终点是否连通,不联通总说明是关键节点 |
PROB Letter Game |
枚举,分两块,先找完整的单词,然后找pair |
PROB Shuttle Puzzle |
刚开始以为搜索,后来看了解题报告,发现原来有规律的,寒啊 |
PROB Pollutant Control |
最小割问题 |
PROB Frame Up |
搜索题,用一张表来维护每个pattern的上下关系,可以大量剪枝 |
TEXT
Minimal Spanning Trees |
最小生成树,经典的算法 |
PROB Agri-Net |
最小生成树,USACO这点比较好,一般讲完了一个算法,都会出一道练习题 |
PROB Score Inflation |
背包问题 |
PROB Humble Numbers |
经典题目,算法是用已有的丑数乘上集合里面的素数去生成新的丑数 |
PROB Shaping Regions |
记得高中的时候做过这道题目,当初用的离散化的方法,不过现在USACO时限改成1秒了,那个方法可能不行了 |
PROB Contact |
枚举,输出有点烦 |
PROB Stamps |
一个背包问题的变形 |
TEXT Knapsack
Problems |
怎么到现在才介绍背包问题啊,前面都有好几道了 |
PROB Factorials |
高精度可以做,但是我是去接保留了最后的6位数,一直到最后。注意只保留一位数是不行的 |
PROB Stringsobits |
直接生成的 |
PROB Spinning Wheels |
又是一个我没看懂题的题目,然后看了标程,原来直接枚举就行了,如此简单 |
PROB Feed Ratios |
线性代数题目,直接把方程解出来就好了 |
PROB Magic Squares |
比较恶心的DFS,主要是转换那个状态起来比较麻烦 |
PROB Sweet Butter |
最短路的题目,枚举每一个点作为集合点,然后求最短路 |
TEXT
Eulerian Tours |
欧拉回路,又是一个经典的算法 |
PROB Riding The Fences |
欧拉回路的题目 |
PROB Shopping Offers |
DP问题,状态方程又不是我自己想的,555~ |
PROB Camelot |
著名的亚瑟王问题,我是看了解题报告才做出来的 |
PROB Home on the Range |
DP问题,找最大子正方形,后面还有一道是找最大子矩形的,难度大了很多 |
PROB A Game |
动态规划,好不容易自己推出来的状态转移方程 |
TEXT
Computational Geometry |
计算几何,没看:( |
PROB Closed Fences |
计算几何的题目,跳过了 |
PROB American Heritage |
二叉树遍历顺序题目,已知前序中序求后序 |
PROB Electric Fence |
一个迭代求最优值的题目,其实就是不断缩小范围的枚举 |
PROB Raucous Rockers |
DP,状态方程又是看来的,似乎这才是比较有难度的DP,不像前面有些题,状态方程简直显而易见 |
TEXT
Graph Theory |
很有用的东西,建议仔细看看 |
TEXT
Flood Fill Algorithms |
其实就是DFS |
PROB The Castle |
Flood
Fill,直接用上面那篇文章的算法就可以过 |
PROB Ordered Fractions |
2次循环,求出所有的分数,约分,去掉重复的,排序 |
PROB Sorting A Three-Valued Sequence |
这题我是看的结题报告,其实就是分块来交换
,首先把所有的能一次交换完成的处理掉,然后处理需要两次交换的 |
PROB Healthy Holsteins |
忘记是贪心还是背包了……-_-! |
PROB Hamming Codes |
直接枚举的 |
TEXT
Data Structures |
跳过 |
TEXT
Dynamic Programming |
动态规划啦,非常有必要好好看,不过这篇文章也只是对于初学者很有用 |
PROB Preface Numbering |
罗马数字问题,把所有可能的组合先生成出来,4,9这种,然后就是求最小表示方法 |
PROB Subset Sums |
背包问题,这题我最开始居然没看出来……,以为是要深搜的,汗啊 |
PROB Runaround Numbers |
直接模拟的,注意判断是否是round
number的条件 |
PROB Party Lamps |
我当初只注意到了每个操作做两次就跟没做一样,所以一共也就有8种操作,后来看了解题报告,发现其实只要处理前6个灯就可以了 |
PROB The Longest Prefix |
DP,我看得别人的解题报告,没办法DP是我的弱项 |
PROB Cow Pedigrees |
DP,自己推了一个差不多的状态方程,可惜错了…… |
PROB Zero Sum |
直接模拟,把表达式生成出来,然后计算结果就行 |
PROB Money Systems |
背包问题 |
PROB Controlling Companies |
看了别人的解题报告,这道题目用了一个变形的Floyd算法,很巧妙 |
TEXT
Shortest Paths |
经典算法啦 |
PROB The Tamworth Two |
模拟吧 |
PROB Overfencing |
其实是比较恶心的一题,因为要转化那个图,剩下的就简单了,从两个exit开始BFS,然后找最大值 |
PROB Cow Tours |
先Floyd,把图划分成两块,然后枚举 |
PROB Bessie Come Home |
直接Floyd就行 |
PROB Fractions to Decimals |
判断时候循环的条件就是看余数是否重复出现,当然,在我看了Analysis之后,发现了更巧妙的办法 |
这个题目是USACO最后的一道题目,我在网上找了N多的题解,不是完全一样的,就是说的不知道是什么东西的
不知道为啥,是不是所有搞OI的人在写题解的时候都喜欢用“提示”的办法,不直接把问题说清楚,而是隐隐约约的公诉你该怎么做,然后你让你自己去想。
于是导致我到现在都没有弄明白这道题目是怎么回事,尽管他们的做法有N多种,但是归根到底都是在搞一个叫做前缀的东西。
下面这个是我在TopCoder上面找到的一个牛人的解释,算是英语写的比较好的,很通俗易懂的
上面这篇文章我想我就不用再翻译了,说的比较清楚,但是他也没有给出具体的算法,不过思路已经很清楚了
其实有了第一条性质之后,最朴素的办法就是枚举i和j,所以个2次循环,但是这样显然是要TLE的
在没有更好的算法的前提下,这道题目的算法就是空间换时间,在我看来就是这样的。
在看了N多代码之后,我觉得还是USACO的Analysis的代码看上去比较美,不知道为啥,那些用Hash和Trie的我都没看懂,可能是我比较菜吧
下面我尝试着把USACO的代码解释一下,看看能不能说清楚,很遗憾,由于这道题目的巨型的输入数据,java肯定是没办法过的
1 int main() {
2 freopen("cowxor.in", "r", stdin);
3 freopen("cowxor.out", "w", stdout);
4 scanf("%i", &n);
5 xr[0] = 0;
6 for (a = 0; a < n; a++ ) {
7 scanf("%d", &x);
8 xr[a+1] = xr[a] ^ x;
9 }
10 for (a = 0; a <= n; a++ ) {
11 prev[a][0] = a-1;
12 prev[a][1] = -1;
13 best[a] = a-1;
14 }
15 wyk = 22;
16 res = 0;
17 while (wyk--) {
18 for (a = 1; a <= n; a++ )
19 if (prev[a][0] == -1)
20 prev[a][1] = -1;
21 else {
22 if (xr[a] >> wyk != xr[prev[a][0]] >> wyk) {
23 tmp[0] = prev[prev[a][0]][1];
24 tmp[1] = prev[a][0];
25 } else {
26 tmp[0] = prev[a][0];
27 tmp[1] = prev[prev[a][0]][1];
28 }
29 prev[a][0] = tmp[0];
30 prev[a][1] = tmp[1];
31 }
32 fnd = false;
33 for (a = 1; a <= n; a++ )
34 if (0 <= best[a])
35 if ((xr[a] >> wyk) % 2 != (xr[best[a]] >> wyk) % 2 ||
36 0 <= prev[best[a]][1])
37 fnd = true;
38 if (fnd) {
39 res |= 1 << wyk;
40 for (a = 1; a <= n; a++ )
41 if (0 <= best[a] &&
42 (xr[a] >> wyk) % 2 == (xr[best[a]] >> wyk) % 2)
43 best[a] = prev[best[a]][1];
44 }
45 }
46 for (a = 0; best[a] == -1; a++ );
47 printf("%d %d %d"n", res, best[a]+1, a);
48 return 0;
49 }
首先,6~9行,程序把从1开始到i位结束的所有的xor都求出来保存在了数组xor里面,我想这个就是为了方便后面用到xor的那个性质。
10~14行是一个 初始化的过程,这个prev的定义应该可以从USACO的Analysis上面看到:
xr[pop[q][0]]'s and xr[q]'s binary representations are the same on
positions e, e+1, etc., and pop[q][0] is biggest possible with 0 ≤
pop[q][0] < q. If there's no such pop[q][0] possible, then pop[q][0]
= -1.
xr[pop[q][1]]'s and xr[q]'s binary representations are the same on
positions e+2, e+2, etc., different on position e, and pop[q][1] is
biggest possible with 0 ≤ pop[q][1] < q. If there's no such
pop[q][1] possible, then pop[q][1] = -1.
我们要根据后面的循环来看这个prev数组的含义
外侧的循环的作用是每次处理一位,由于题目中已经说明了,最多只有21位,所以循环21次就可以了。
我们来看内侧的循环
18~31行,对所有的奶牛编号循环一遍,得到的结果就是:
对于当前的xor number,对于这个xor number的前21 - wyk位,与他相同的那个xor number的位置,并且,这个位置要尽量的靠后。
如果没有相同的,那么这个位置就是-1
这样,经过每次循环之后,prev里面就是与当前的xor number前k位相同的那个数的位置,一次一次循环,直到最后。
prev[i][0]存的是当current bit也相同的时候的位置,prev[i][1]存的是currnet bit不相同时候的位置,为什么要这样呢?
这可能就需要解释一下
if (xr[a] >> wyk != xr[prev[a][0]] >> wyk) {
tmp[0] = prev[prev[a][0]][1];
tmp[1] = prev[a][0];
} else {
tmp[0] = prev[a][0];
tmp[1] = prev[prev[a][0]][1];
}
如果当前比较的这一位相等,那么也就意味着prev[a][0]不用变,但是prev[i][1]却必须变化,因为当前的这一位,已经不是刚才比较的那一位了,prev[a][1]应该等于此时的prev[a][0]的prev[a][1],我想这个应该不难理解。
另一方面,如果当前比较的这一位不相等的时候,那么prev[a][1]就应该等于prev[a][0]了,因为之前的所有位都相等,之后当前这一位不相
等,这就是prev[a][1]的定义。那么prev[a][0]的值呢?我们需要注意的时候,当我们处理到a的时候,prev[a][0]位置的值肯定
是已经处理过的了。那么prev[a][prev[a][0]][1]里面存的就是与prev[a][0]在当前位不相等的那个xor
number的位置,注意,bit只有0和1,prev[a][0]与当前的不相等,而prev[a][prev[a][0]][1]又与prev[a]
[0]不相等,所以当前的prev[a][1]肯定与prev[a][prev[a][0]][1]是相等的。这就是 tmp[0] =
prev[prev[a][0]][1];的含义。
我再来看32~37行,首先要知道best[q]的定义:
if X would be equal biggest possible xor, then xr[best[q]] xor xr[q]'s
in equal X's binary representation on positions e, e+1, etc., and
best[q] is biggest possible with 0 ≤ best[q] < q. If there's no such
best[q] possible, then best[q] = -1.
想要得到尽量大的xor,那么就要尽量让每一位都不相同 (xr[a] >> wyk) % 2就是取当前的二进制的最后一位
这段代码的作用就是找,到当前位为止,是否有两个xor number,他们的每一位都不相同,这样就能保证异或结果是最大的。
接下来看38~45的这段代码,因为我们是从高位到低位来处理的,这样的话,如果能保证高位是1,那么比你所有的低位都是1都管用:)
于是,既然我们找到了高位是1的情况,那么我们就要记录下结果 res
然后,剩下的那个循环就是更新best[a]
在所有的xor
number中,如果当前位跟best[a]的当前位是相等的,那么就更新best[a],将其更新为prev[best[a]][1],就是除了当前位
不相等,其余位不变的那个xor number,这样就保证了从高位开始,每一位都能够尽量取到1,因为只要高位尽量是1,我们的结果就能尽量的大。
其实prev这个数组里面真正有用的是prev[q][1]
不知道我解释清楚了没,其实解释了一遍,自己也清楚了很多。
Section 1.0 |
TEXT
Introduction |
介绍啦,我是没看 |
Section 1.1 |
TEXT
Submitting Solutions |
交你怎么提交程序的,可以看看 |
PROB Your Ride Is
Here |
最直接的方法是直接乘,然后mod 47,不过可以利用余数定理,边乘边mod |
TEXT Contest
Problem Types |
跳过 |
TEXT Ad Hoc
Problems |
跳过 |
PROB Greedy Gift Givers |
简单的模拟题,就是处理名字的时候有点烦 |
PROB Friday the Thirteenth |
数日期的题,我不知道一天天的模拟能不能过,我是只算了周五这一天的。 |
PROB Broken Necklace |
也是模拟题,不过很要细心,有很多特殊情况,比如全是w。 |
Section 1.2 |
TEXT
Complete Search |
跳过 |
PROB Milking Cows |
直接模拟应该是过不了的, |
PROB Transformations |
模拟题,直接把所有可能的pattern生成出来,然后比较就行 |
PROB Name That Number |
正确方法是把字典里面的所有word转化成数字,然后比较就行。 |
PROB Palindromic Squares |
直接枚举 |
PROB Dual Palindromes |
DFS,注意搜索的时候,只要搜索回文数前一半就行,后面的直接反向复制一下就好 |
Section 1.3 |
TEXT
Greedy Algorithm |
跳过 |
PROB Mixing Milk |
简单的贪心 |
PROB Barn Repair |
也是贪心法,把最大的缝隙就出来,然后去覆盖 |
TEXT Winning
Solutions |
跳过 |
PROB Calf Flac |
枚举,从没一点向两边枚举 |
PROB Prime Cryptarithm |
直接枚举,反正只有5个数 |
Section 1.4 |
TEXT
More Search Techniques |
跳过 |
PROB Packing Rectangles |
恶心题,我没做:P |
PROB The Clocks |
看了一个牛人的结题报告后过的,那位牛人总结了一个数组,就是如何让表针转一圈回到原来位置的操作组合 |
PROB Arithmetic Progressions |
搜索,硬搜的 |
PROB Mother's Milk |
BFS,把所有的情况都弄出来 |
Section 1.5 |
TEXT
Introduction to Binary Numbers |
跳过 |
PROB Number Triangles |
经典DP |
PROB Prime Palindromes |
搜索,生成回文数,检查是否是素数。需要一点点剪枝(长度是偶数的回文数,除了11之外必然是合数,因它肯定是11的倍数) |
PROB SuperPrime Rib |
直接枚举 |
PROB Checker Challenge |
八皇后啊,用最经典的算法就能过,不过如果想优化的非常快,可能需要其他的办法,也有很复杂的。 |
感想及题解待会儿再发:)
1 import java.io.*;
2 import java.util.*;
3 /*
4 ID: yanglei4
5 LANG: JAVA
6 TASK:picture
7 */
8 public class picture {
9 public int[] level;
10 public int N;
11 public int ans = 0;
12
13 public class Line implements Comparable<Line> {
14 int s,t,p;
15 boolean start;
16 public Line(int a, int b, int c, boolean d) {
17 s = a;
18 t = b;
19 p = c;
20 start = d;
21 }
22 public int compareTo(Line o) {
23 if (this.p == o.p) {
24 if (this.start)
25 return -1;
26 else
27 return 1;
28 }
29 return this.p < o.p ? -1 : 1;
30 }
31 }
32 public void Scan(Line[] L) {
33 for (int i = 0;i <= 20000;i++)
34 level[i] = 0;
35 for (int i = 0; i < 2 * N;i++) {
36 if (L[i].start) {
37 for (int j = L[i].s;j < L[i].t;j++) {
38 level[j]++;
39 if (level[j]==1)
40 ans++;
41 }
42 }
43 else {
44 for (int j = L[i].s;j < L[i].t;j++) {
45 level[j]--;
46 if (level[j]==0)
47 ans++;
48 }
49 }
50 }
51 }
52 public void done() throws IOException {
53 BufferedReader f = new BufferedReader(new FileReader("picture.in"));
54 PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter("picture.out")));
55 N = Integer.parseInt(f.readLine());
56 Line[] Lx = new Line[2 * N];
57 Line[] Ly = new Line[2 * N];
58
59 for (int i = 0; i < N; i++) {
60 StringTokenizer st = new StringTokenizer(f.readLine());
61 int x1 = Integer.parseInt(st.nextToken());//left x
62 int y1 = Integer.parseInt(st.nextToken());//left y
63 int x2 = Integer.parseInt(st.nextToken());//right x
64 int y2 = Integer.parseInt(st.nextToken());//right y
65 x1 += 10000;
66 y1 += 10000;
67 x2 += 10000;
68 y2 += 10000;
69 Lx[2 * i] = new Line(x1,x2,y1,true);
70 Lx[2 * i + 1] = new Line(x1,x2,y2,false);
71 Ly[2 * i] = new Line(y1,y2,x1,true);
72 Ly[2 * i + 1] = new Line(y1,y2,x2,false);
73 }
74 Arrays.sort(Lx);
75 Arrays.sort(Ly);
76 level = new int[20001];
77 Scan(Lx);
78 Scan(Ly);
79 out.println(ans);
80
81 out.close();
82 }
83 public static void main(String[] args) throws IOException {
84 picture t = new picture();
85 t.done();
86 System.exit(0);
87 }
88 }
89
这道题用了离散化的方法
其实最朴素的方法就是在一个20000*20000的矩阵上面把所有的点描出来,然后把这些点的周长算出来,不过算周长这一步也是很麻烦的。
因为毕竟还有可能出现“空心”的情况
用离散化的方法就是想办法只数每条线段,而不去管其他空白的地方是怎么样的。
由于我们是需要算周长的,所以只需要看矩形的四条边就可以了。
然后,我们就是先看所有的横边,再看竖边。
先把横边全部选出来,放在一个数组里面,然后排序,从下到上。
一个矩形的两条边要分成始边和终边,排序的时候,如果纵坐标相同,那么应该把始边放在终边。
如果遇到始边,那么就把这条边上面的所有点level[j]加一。
如果遇到终边,就把那条边上所有的点的level[j]减一
于是,当一条边上的点的leve是1的时候,那么就说明这条边肯定是始边边缘,所以要ans++
另一种情况,当一条边上的点的level是0的时候,那么说明这个点是终边边缘,所以ans++
这样扫完横边再扫竖边。
最后的ans就是周长了。
不过这个算法的时间效率并不是非常的好,因为数据比较弱(可能是这样)
如果真的是有5000个矩形,没个矩形都是20000×20000的,那么可能会超时
虽然从5.1开始,大部分题目都要借助于NoCow和网上的解题报告,但是还是学到了不少的东西。
原来认为只要熟练的掌握各种算法,那就可以随便去切题,现在发现其实不是这样。
有很多题目,都需要进行一些转化,也可以说是建模,才能套用现成的算法
而有一些题目,根本就没有现成的算法,只能你自己去想
这种算法基本上是不属于任何一类的
还有一些比如说剪枝,虽然搜索谁都会,Brute Force谁都会写,但是剪枝却不是谁都能写的出来的
这就需要一些数学功底
现在发现这个东西必须要长年累月的积累才能够达到驾轻就熟的境界。
但是就算那样,也不能保证所有的题目都会做。
1 import java.io.*;
2 import java.util.*;
3 /*
4 ID:
5 LANG: JAVA
6 TASK:telecow
7 */
8 public class telecow {
9
10 public static final int WHITE = 0, GRAY = 1, BLACK = 2;
11 private int[][] flow, capacity, res_capacity;
12 private int[] parent, color, queue;
13 private int[] min_capacity;
14 private int size, source, sink, first, last;
15 private static int MAXN = 200;
16 int N,M;
17
18 private int maxFlow() // Edmonds-Karp algorithm with O(V3E) complexity
19 {
20 flow = new int[MAXN][MAXN];
21 res_capacity = new int[MAXN][MAXN];
22 parent = new int[MAXN];
23 min_capacity = new int[MAXN];
24 color = new int[MAXN];
25 queue = new int[MAXN];
26 int max_flow = 0;
27 for (int i = 0; i < size; i++)
28 for (int j = 0; j < size; j++)
29 res_capacity[i][j] = capacity[i][j];
30
31 while (BFS(source))
32 {
33 max_flow += min_capacity[sink];
34 int v = sink, u;
35 while (v != source)
36 {
37 u = parent[v];
38 flow[u][v] += min_capacity[sink];
39 flow[v][u] -= min_capacity[sink];
40 res_capacity[u][v] -= min_capacity[sink];
41 res_capacity[v][u] += min_capacity[sink];
42 v = u;
43 }
44 }
45 return max_flow;
46 }
47
48 private boolean BFS(int source) // Breadth First Search in O(V2)
49 {
50 for (int i = 0; i < size; i++) {
51 color[i] = WHITE;
52 min_capacity[i] = Integer.MAX_VALUE;
53 }
54
55 first = last = 0;
56 queue[last++] = source;
57 color[source] = GRAY;
58
59 while (first != last) // While "queue" not empty..
60 {
61 int v = queue[first++];
62 for (int u = 0; u < size; u++)
63 if (color[u] == WHITE && res_capacity[v][u] > 0)
64 {
65 min_capacity[u] = Math.min(min_capacity[v], res_capacity[v][u]);
66 parent[u] = v;
67 color[u] = GRAY;
68 if (u == sink) return true;
69 queue[last++] = u;
70 }
71 }
72 return false;
73 }
74
75 public void done() throws IOException {
76 BufferedReader f = new BufferedReader(new FileReader("telecow.in"));
77 PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter("telecow.out")));
78 StringTokenizer st = new StringTokenizer(f.readLine());
79 N = Integer.parseInt(st.nextToken());
80 M = Integer.parseInt(st.nextToken());
81 source = Integer.parseInt(st.nextToken());
82 sink = Integer.parseInt(st.nextToken());
83 source--;
84 sink--;
85 int osource = source;
86 int osink = sink;
87 capacity = new int[N * 2][N * 2];
88
89 //init
90 for (int i = 0; i < N; i++)
91 capacity[i * 2][i * 2 + 1] = 1;
92
93 for (int i = 0; i < M; i++) {
94 st = new StringTokenizer(f.readLine());
95 int x = Integer.parseInt(st.nextToken());
96 int y = Integer.parseInt(st.nextToken());
97 x--;
98 y--;
99 capacity[x * 2 + 1][y * 2] = Integer.MAX_VALUE;
100 capacity[y * 2 + 1][x * 2] = Integer.MAX_VALUE;
101 }
102
103 for (int i = 0; i < 2 * N; i++) {
104 capacity[i][source * 2] = 0;
105 capacity[sink * 2 + 1][i] = 0;
106 }
107
108 int[] ans = new int[N + 1];
109
110 size = 2 * N;
111 source = source * 2 + 1;
112 sink = sink * 2;
113
114 int max = maxFlow();
115
116 int count = 0;
117 for (int i = 0; i < N; i++)
118 if (i != osource && i != osink) {
119 capacity[i * 2][i * 2 + 1] = 0;
120 int nowFlow = maxFlow();
121
122 if (max - nowFlow == count + 1)
123 ans[count++] = i;
124 else
125 capacity[i * 2][i * 2 + 1] = 1;
126 }
127
128 out.println(max);
129
130 for (int i = 0; i < count - 1; i++)
131 out.print(ans[i] + 1 + " ");
132 out.println(ans[count - 1] + 1);
133 out.close();
134
135 }
136
137 public static void main(String[] args) throws IOException {
138 telecow t = new telecow();
139 t.done();
140 System.exit(0);
141 }
142 }
143
这道题目我自己看出来是最小割的问题,而之前的题目有一道是最小割的
但是有一个不同,这道题是点的最小割,而不是常规的边的最小割
所以这就比较麻烦,需要我们把点转化成边
这就让我想起了之前的那个Fence Loops
我就是把所有的边表示的图转化成了点表示的图,我实在是不想再写那么恶心的算法了。
然后我就去NoCow上面看了一下,果然是有很赞的方法。
具体方法就是,把一个点变成两个点,然后在两个点之间加一条边,并且这条边的权值是1
这样的话,如果割这条边,就相当于去掉了这个点。
剩下的事情就是跟前面的那个Pollute Control差不多了
每次去掉一条边,然后用MaxFlow求一下最大流,如果得出的最大流==最大流-边的权值
那么就证明这条边是最小割。
就这样把所有的最小割找出来,然后就可以了
貌似数据很弱,不像上次的那个Pollute那道题目,还要考虑边的编号的问题的。
一个搜索的题目,不过肯定是要剪枝的
最简单的两个剪枝我想到了
第一个:
首先,第一行已经确定了,那么我们可以把第一列也确定,就按照升序,2,3,4,5……,这样的话,没生成这么一个square,我们就可以随便的去交换除了第一行后面的几行。
他们的一个全排列就对应着一种组合,所以最后的答案乘以N-1的阶乘就可以
第二个:
这个其实是看来的,就是每次只要所搜完N-1行就可以了。因为剩下的一行必然存在一个解。其实这个不难理解的,最后一行的排列顺序只跟前面的每一列缺少的那个数有关。
而且也只能取缺少的那个数,所以也就是唯一的。
第三个:
要用到置换群,我没看懂
尽管之前N次看组合数学里面都说到置换群,但是我还是似懂非懂,问了数学小王子,他也不是非常懂。然后以为这个东西没用,就忽略了。没想到还真的有用。
今天终于第一次写了强连通分量的题目
虽然以前老早就知道有这么个东西,对这个东西也有概念,但是确实从来没有写过判别的算法
原来如此简单,但是确实也很巧妙。
在算法导论上面看到的K算法,方法就是做两遍DFS,因为强连通分量有一个性质,就是如果把所有的边反向,那么它仍然是一个强连通分量。
但是今天我用的是Tarjan算法,据说效率比K算法高很多,事实上也应该是这样,因为Tarjan算法只做了一次DFS
其实思想也很简单,就是一直DFS(u),向下向下向下,如果突然发现有一个点可以回到u了,那么这个肯定是一个强连通分量。
哈哈,是不是很通俗。
严格的定义过程大家可以看这里http://www.cmykrgb123.cn/blog/scc-tarjan/
我也是参考了这个才做出来的Tarjan算法,Wiki上的那个过于庞大了,虽然封装的非常好
说说本题,其实就是找强连通分量,然后把每个强连通分量当成一个“超点”来考虑
如果这个“超点”的入度为零,那么它就必然是一个源头,统计这样的“超点”的个数,就是第一问的答案。
第二问,如果有一个“超点”入度不是0,但是出度是0,那就说明这个“超点”可以给其他“超点”作贡献。
我们就找这样的点对,把入度是0和出度是0的点连起来。
这样匹配过后,剩下的要么全是入度是0的,要么全是出度是0的,这些就没办法了,随便从一个连通分量连接过来一条边就可以了。
所以第二问的答案就是所有的“超点”中,入度是0和出度是0的点大的那个数。
1 import java.io.*;
2 import java.util.*;
3 /*
4 ID: yanglei4
5 LANG: JAVA
6 TASK:schlnet
7 */
8 public class schlnet {
9 boolean[] inStack;
10 int[] DFN;
11 int[] LOW;
12 int index = 0;
13 int[] Stack;
14 int top = 0;
15 int[][] map;
16 int BelongCount = 0;
17 int[] belong;
18
19 public void Tarjan(int i) {
20 DFN[i] = LOW[i] = ++index;
21 inStack[i] = true;
22 Stack[top++] = i;
23 for (int j = 1; j <= map[i][0]; j++) {
24 if (DFN[map[i][j]] == 0) {
25 Tarjan(map[i][j]);
26 if (LOW[map[i][j]] < LOW[i])
27 LOW[i] = LOW[map[i][j]];
28 }
29 else if (inStack[map[i][j]] && DFN[map[i][j]] < LOW[i])
30 LOW[i] = DFN[map[i][j]];
31 }
32 if (DFN[i] == LOW[i]) {
33 int j = 0;
34 do {
35 j = Stack[--top];
36 inStack[j] = false;
37 belong[j] = BelongCount;
38 } while (j != i);
39 BelongCount++;
40 }
41
42 }
43
44 public void done() throws IOException {
45 BufferedReader f = new BufferedReader(new FileReader("schlnet.in"));
46 PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter("schlnet.out")));
47 int N = Integer.parseInt(f.readLine());
48 map = new int[N + 1][N + 1];
49 DFN = new int[N + 1];
50 LOW = new int[N + 1];
51 inStack = new boolean[N + 1];
52 Stack = new int[N + 1];
53 belong = new int[N + 1];
54
55 Arrays.fill(belong,-1);
56 for (int i = 1; i <= N; i++) {
57 StringTokenizer st = new StringTokenizer(f.readLine());
58 int len = st.countTokens();
59 map[i][0] = len - 1;
60 for (int j = 1; j <= len - 1; j++)
61 map[i][j] = Integer.parseInt(st.nextToken());
62 }
63
64 for (int i = 1; i <= N; i++) {
65 if (DFN[i] == 0)
66 Tarjan(i);
67 }
68 boolean[][] dis = new boolean[BelongCount + 1][BelongCount + 1];
69 for (int i = 1;i <= N;i++) {
70 for (int k = 1;k <= map[i][0];k++)
71 dis[belong[i] + 1][belong[map[i][k]] + 1] = true;
72 }
73 int[] oud = new int[BelongCount + 1];
74 int[] ind = new int[BelongCount + 1];
75
76 for (int i = 1;i <= BelongCount;i++) {
77 for (int j = 1; j<= BelongCount;j++) {
78 if (i!=j && dis[i][j]) {
79 oud[i]++;
80 ind[j]++;
81 }
82 }
83 }
84
85 int i0 = 0;
86 int o0 = 0;
87 for (int i = 1;i <= BelongCount;i++) {
88 if (ind[i] == 0)
89 i0++;
90 if (oud[i] == 0)
91 o0++;
92 }
93
94 if (BelongCount == 1) {
95 out.println("1");
96 out.println("0");
97 }
98 else {
99 out.println(i0);
100 out.println(i0>o0?i0:o0);
101 }
102 out.close();
103 }
104
105 public static void main(String[] args) throws IOException {
106 schlnet t = new schlnet();
107 t.done();
108 System.exit(0);
109 }
110 }
111
1 import java.io.*;
2 import java.util.*;
3
4 /*
5 ID: yanglei4
6 LANG: JAVA
7 TASK:bigbrn
8 */
9 public class bigbrn {
10 public static int min(int a, int b) {
11 if (a < b) return a;
12 else return b;
13 }
14 public static int min3(int a, int b, int c) {
15 return min(a, min(b, c));
16 }
17 public static void main(String[] args) throws IOException {
18 BufferedReader f = new BufferedReader(new FileReader("bigbrn.in"));
19 PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter("bigbrn.out")));
20 StringTokenizer st = new StringTokenizer(f.readLine());
21 int N = Integer.parseInt(st.nextToken());
22 int T = Integer.parseInt(st.nextToken());
23 int[][] map = new int[N][N];
24 for (int i = 0; i < T; i++) {
25 st = new StringTokenizer(f.readLine());
26 int x = Integer.parseInt(st.nextToken());
27 int y = Integer.parseInt(st.nextToken());
28 map[x - 1][y - 1] = -1;
29 }
30
31 for (int i = 0; i < N; i++) {
32 if (map[0][i]!= -1)
33 map[0][i] = 1;
34 if (map[i][0]!= -1)
35 map[i][0] = 1;
36 }
37
38
39 for (int i = 1; i < N; i++)
40 for (int j = 1; j < N; j++) {
41 if (map[i][j] != -1) {
42 int temp = min3(map[i - 1][j], map[i][j - 1], map[i - 1][j - 1]);
43 if (temp != -1) map[i][j] = temp + 1;
44 else map[i][j] = 1;
45 }
46 }
47
48 int max = 0;
49 for (int i = 0; i < N; i++)
50 for (int j = 0; j < N; j++)
51 if (map[i][j] != 0 && map[i][j] > max)
52 max = map[i][j];
53
54 out.println(max);
55 out.close();
56 System.exit(0);
57 }
58 }
59
应该算是一个比较基本的DP吧,状态转移方程也不难想,但是我最开始写成了N^3的了
首先就是用Map[i][j]来表示以i,j为右下角的最大的square的大小
初始化就是第一行,第一列,如果不是#,那么肯定是1
然后对于i,j,我们需要观察i - 1,j - 1,因为是square,所以只跟这个有关
如果在第i行,第j列上面,从当前位置开始,连续map[i-1][j-1]个位置,没有#的话,那么map[i][j] = map[i-1][j-1]+1
我就是在判断连续map[i-1][j-1]这个地方出了问题,我又加了一层循环,所以就变成了N^3的了,然后果然TLE了
这个地方完全没必要用循环一个一个去判断,因为其实你已经有结果了,这个结果就是map[i-1][j]和map[i][j-1]里面小的那个
map[i-1][j]肯定就是从当前位置开始,在第j列上,向上最多可以走多少步不碰到#
因为这时候实际上你已经确定了,#只有可能出现在第i行,第j列上,因为map[i-1][j-1]不是#就保证了这一点
于是,找到两个方向上走的比较近的那个数,如果这个数是小于map[i-1][j-1]的,那么map[i][j]就等于这个数
否则,map[i][j] = map[i-1][j-1]+1
这个地方的重点就是,如果map[i-1][j-1]不是#,那么就保证了#只能在第i行,第j列上面。
只需要检查这两个就可以
然后我们就可以来看map[i-1][j],map[i][j-1],这两个东西其实跟map[i-1][j-1]共享了上面的一块。
如果在第i行,第j列上面出现了#,那么map[i-1][j],map[i][j-1]肯定比map[i-1][j-1]小
否则,我们的square最大也就只能是map[i-1][j-1]+1,因为map[i-1][j-1]已经是以i-1,j-1为右下角最大的square了
于是状态转移方程就是
map[i][j] = min (map[i-1][j],map[i][j-1],map[i-1][j-1]) + 1, map[i][j] != '#'
摘要: 绝对,非常考耐心,细心的一道题目,这道题目充分的说明我是考虑问题不全面的人
所以现在看来TopCoder的测试数据还都算是比较弱的,真的没有极其变态的,而且TopCoder的题目其实没有超级复杂的,或许是因为我从来没做过第三题吧。
回正题。
这道题目其实一看就知道怎么做,但是就一个字烦
首先你要用FloodFill把所有的Cluster标注出来,这个不难,而且速度很快,时间... 阅读全文
发个网络流的标准代码,用的是BFS
外面自己套一个类就行了
capacity自己初始化放进去就可以了
返回值在max_flow里面,有需要的话可以自己改成返回类型的
1 public static final int WHITE = 0, GRAY = 1, BLACK = 2;
2 private int[][] flow, capacity, res_capacity;
3 private int[] parent, color, queue;
4 private int[] min_capacity;
5 private int size, source, sink, first, last;
6 private int max_flow;
7
8 private void maxFlow() // Edmonds-Karp algorithm with O(V3E) complexity
9 {
10 flow = new int[size][size];
11 res_capacity = new int[size][size];
12 parent = new int[size];
13 min_capacity = new int[size];
14 color = new int[size];
15 queue = new int[size];
16
17 for (int i = 0; i < size; i++)
18 for (int j = 0; j < size; j++)
19 res_capacity[i][j] = capacity[i][j];
20
21 while (BFS(source))
22 {
23 max_flow += min_capacity[sink];
24 int v = sink, u;
25 while (v != source)
26 {
27 u = parent[v];
28 flow[u][v] += min_capacity[sink];
29 flow[v][u] -= min_capacity[sink];
30 res_capacity[u][v] -= min_capacity[sink];
31 res_capacity[v][u] += min_capacity[sink];
32 v = u;
33 }
34 }
35 }
36
37 private boolean BFS(int source) // Breadth First Search in O(V2)
38 {
39 for (int i = 0; i < size; i++)
40 {
41 color[i] = WHITE;
42 min_capacity[i] = Integer.MAX_VALUE;
43 }
44
45 first = last = 0;
46 queue[last++] = source;
47 color[source] = GRAY;
48
49 while (first != last) // While "queue" not empty..
50 {
51 int v = queue[first++];
52 for (int u = 0; u < size; u++)
53 if (color[u] == WHITE && res_capacity[v][u] > 0)
54 {
55 min_capacity[u] = Math.min(min_capacity[v], res_capacity[v][u]);
56 parent[u] = v;
57 color[u] = GRAY;
58 if (u == sink) return true;
59 queue[last++] = u;
60 }
61 }
62 return false;
63 }
原来这道题目这么简单,记得当年高中的时候做USACO的时候,好像最后就是卡在这道题目上面,然后就是NOIP了
然后我就伤心的告别了NOIP投向了好好学习的怀抱。
今天拿过来看还是没什么思路,然后就去搜了一下解题报告。
其实这个题目无论如何也是要DFS的,因为人家要你输出全部的答案。这样就不用怕了,最多就是剪剪枝。
但是最开始没有想到这一点。
问题就在这个剪枝上,和怎么判断合理的解上面。
方法就是,首先找到每一个Frame的四个角。
然后沿着这个Frame的每条边走一下,如果有其他的字符,就是跟当前Frame不同的字符,那么这个字符肯定是在当前这个字符上层的。
这样就能用一张表来确定上下关系,Above[i][j],表示第i个字符在第j个字符上层
然后就是根据这张表来做一个DFS,这样就可以了。
看了leokan的解题报告,说还要floyd一下。
这样做的目的无非也就是想确定两两之间的上下层关系罢了。
但是似乎没这个必要,直接DFS就可以了。
1 import java.io.*;
2 import java.util.*;
3 /*
4 ID: yanglei4
5 LANG: JAVA
6 TASK:frameup
7 */
8 public class frameup{
9 static char[][] board;
10 static int[][] pos;
11 static int[] in;
12 static int cnt;
13 static int[][] above;
14 static char[] answer;
15 static PrintWriter out;
16 public static void findAnswer(int step) {
17 int i,j;
18 if (step >= cnt) {
19 for (int k = 0; k < cnt; k++)
20 out.print(answer[k]);
21 out.println();
22 }
23 for (i = 0; i < 26; i++)
24 if (in[i]> 0) {
25 for (j = 0; j < 26; j++)
26 if (in[j] > 0 && above[i][j] == 1) break;
27
28 if (j >= 26) { /* no such frame, so this COULD be the next one */
29 answer[step] = (char) ('A' + i);
30 in[i] = 0; /* mark as in answer */
31 findAnswer(step + 1);
32 in[i] = 1; /* clear mark */
33 }
34
35 }
36 }
37 public static void main(String[] args) throws IOException {
38 BufferedReader f = new BufferedReader(new FileReader("frameup.in"));
39 out = new PrintWriter(new BufferedWriter(new FileWriter("frameup.out")));
40 StringTokenizer st = new StringTokenizer(f.readLine());
41 int H = Integer.parseInt(st.nextToken());
42 int W = Integer.parseInt(st.nextToken());
43
44 board = new char[30][32];
45 pos = new int[26][4];
46 in = new int[26];
47 cnt = 0;
48
49 above = new int[26][26];
50 answer = new char[27];
51
52 for (int i = 0; i < H; i++) {
53 String temp = f.readLine();
54 board[i] = temp.toCharArray();
55 for (int j = 0; j < W; j++) {
56 if (board[i][j] != '.') {
57 int loc = board[i][j] - 'A';
58
59 if (in[loc] == 0) {//First time met
60 in[loc] = 1;
61 cnt++;
62 pos[loc][0] = pos[loc][2] = i;
63 pos[loc][1] = pos[loc][3] = j;
64 }
65 else {
66 if (i < pos[loc][0]) pos[loc][0] = i;
67 if (i > pos[loc][2]) pos[loc][2] = i;
68 if (j < pos[loc][1]) pos[loc][1] = j;
69 if (j > pos[loc][3]) pos[loc][3] = j;
70 }
71
72 }
73 }
74 }
75
76 for (int i = 0; i < 26; i++)
77 if (in[i] > 0) { /* for each frame */
78 for (int j = pos[i][0]; j <= pos[i][2]; j++) { /* check left and right borders */
79
80 /* left */
81 int loc = board[j][pos[i][1]] - 'A';
82 if (loc != i) /* there's a different frame where this one should be */
83 above[loc][i] = 1; /* that different frame must be above this one */
84
85 /* right */
86 loc = board[j][pos[i][3]] - 'A';
87 if (loc != i) /* same as left */
88 above[loc][i] = 1;
89 }
90 for (int j = pos[i][1]; j <= pos[i][3]; j++) { /* check top and bottom */
91
92 /* top */
93 int loc = board[pos[i][0]][j] - 'A';
94 if (loc != i)
95 above[loc][i] = 1;
96
97 /* bottom */
98 loc = board[pos[i][2]][j] - 'A';
99 if (loc != i)
100 above[loc][i] = 1;
101 }
102 }
103
104 findAnswer(0);
105 out.close();
106 System.exit(0);
107 }
108 }
109
Principles of Modeling
- First, The choice of what models to create has
a profound influence on how a problem is attacked and how a solution is
shaped.
- Second, Every model may be expressed at
different levels of precision.
- Third, The best models are connected to
reality.
- Fourth,No single model or view is sufficient.
Every nontrivial system is best approached through a small set of nearly
independent models with multiple viewpoints.
Three major elements:
- the UML's basic building blocks
- the rules that dictate
how those building blocks may be put together
- some common mechanisms that
apply throughout the UML
The vocabulary of the UML encompasses three kinds of building blocks:
-
Things
-
Relationships
-
Diagrams
Three kinds of relationships that are most important: dependencies, generalizations, and associations.
- Dependencies are using relationships. For example, pipes depend on the water heater to heat the water they carry.
- Associations are structural relationships among instances. For example, rooms consist of walls and other things; walls themselves may have embedded doors and windows; pipes may pass through walls.
- Generalizations connect generalized classes to more-specialized ones in what is known as subclass/superclass or child/parent relationships. For example, a picture window is a kind of window with very large, fixed panes; a patio window is a kind of window with panes that open side to side.
先自动下载了新的控件
然后去用这个文件去注册掉那个什么什么CAB
http://www.blogjava.net/Files/LittleDS/800A138F.rar
然后再去下载 xenroll.dll
去 cmd 运行 regsvr32 xenroll.dll
然后就可以了,就可以导入数字证书了。
摘要: Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
--> 1 import java.io.*;
2 import java.util.*;
&n... 阅读全文
速度提高了一倍啊,看来底层的东西还是有必要了解的,不过Java写东西是真方便啊
本来这道题是要高精度的,YD啊,但是Java有BigDecimal,哈哈,不过这玩意好用是好用,但是确实是非常的慢
我估计我自己用一个数组实现的话,速度应该还会快的
不过最后被我瞎猫碰死耗子碰到了,终于过了。
1 import java.io.*;
2 import java.math.BigInteger;
3 import java.util.*;
4 /*
5 ID: yanglei4
6 LANG: JAVA
7 TASK:buylow
8 */
9 public class buylow{
10 public static void main(String[] args) throws IOException {
11 BufferedReader f = new BufferedReader(new FileReader("buylow.in"));
12 PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter("buylow.out")));
13 int N = Integer.parseInt(f.readLine());
14 int[] D = new int[N];
15 int count = 0;
16 String temp = f.readLine();
17 while (temp != null) {
18 StringTokenizer st = new StringTokenizer(temp);
19 int len = st.countTokens();
20 for (int i = 0; i < len; i++)
21 D[count++] = Integer.parseInt(st.nextToken());
22 temp = f.readLine();
23 }
24 long[] lds = new long[N];
25 BigInteger[] cnt = new BigInteger[N];
26 for (int i = 0; i < N; i++) {
27 lds[i] = 1;
28 cnt[i] = new BigInteger("1");
29 }
30 ArrayList<Integer> exist = new ArrayList<Integer>();
31 for (int i = 0; i < N; i++) {
32
33 for (int j = i - 1; j >= 0; j--) {
34 if (D[i] < D[j]) {
35 if (lds[j] + 1 > lds[i]) {
36 lds[i] = lds[j] + 1;
37 exist.clear();
38 exist.add(D[j]);
39 cnt[i] = new BigInteger(cnt[j].toByteArray());
40 }
41 else if (lds[j] + 1 == lds[i]) {
42 if (!exist.contains(D[j])) {
43 exist.add(D[j]);
44 cnt[i] = cnt[i].add(cnt[j]);
45 }
46 }
47
48 }
49 }
50 }
51
52 /* for (int i = 0; i < N; i++)
53 System.out.println(lds[i][0] + " " + lds[i][1] + " " + cnt[i]);*/
54
55 long maxleng = 0;
56 for (int i = 0; i < N; i++) {
57 if (maxleng < lds[i])
58 maxleng = lds[i];
59 }
60 BigInteger sum = new BigInteger("0");
61 exist = new ArrayList<Integer>();
62 for (int i = N - 1; i >= 0; i--) {
63 if (lds[i] == maxleng)
64 if (!exist.contains(D[i])) {
65 sum = sum.add(cnt[i]);
66 exist.add(D[i]);
67 }
68 }
69
70 out.println(maxleng + " " + sum);
71 out.close();
72 System.exit(0);
73 }
74 }
75
就是红色的那个地方,用toString的话,最后一个点过不了,换成了这个就过了。
1 if (source = sink)
2 totalflow = Infinity
3 DONE
4 totalflow = 0
5 while (True)
6 # find path with highest capacity from
# source to sink
7 # uses a modified djikstra's algorithm
8 for all nodes i
9 prevnode(i) = nil
10 flow(i) = 0
11 visited(i) = False
12 flow(source) = infinity
13 while (True)
14 maxflow = 0
15 maxloc = nil
16 # find the unvisited node with
# the highest capacity to it
17 for all nodes i
18 if (flow(i) > maxflow AND
not visited(i))
19 maxflow = flow(i)
20 maxloc = i
21 if (maxloc = nil)
22 break inner while loop
23 if (maxloc = sink)
24 break inner while loop
24a visited(maxloc) = true
25 # update its neighbors
26 for all neighbors i of maxloc
27 if (flow(i) < min(maxflow,
capacity(maxloc,i)))
28 prevnode(i) = maxloc
29 flow(i) = min(maxflow,
capacity(maxloc,i))
30 if (maxloc = nil) # no path
31 break outer while loop
32 pathcapacity = flow(sink)
33 totalflow = totalflow + pathcapacity
# add that flow to the network,
# update capacity appropriately
35 curnode = sink
# for each arc, prevnode(curnode),
# curnode on path:
36 while (curnode != source)
38 nextnode = prevnode(curnode)
39 capacity(nextnode,curnode) =
capacity(nextnode,curnode) -
40 pathcapacity
41 capacity(curnode,nextnode) =
capacity(curnode,nextnode) +
42 pathcapacity
43 curnode = nextnode
第一次用Java代码过搜索+剪枝的题目啊,不容易啊……,而且还是参考了后面的Analysis,-_-!
不过里面的那个Hash的剪枝还是很强大的,再一次说明了学好数学的重要
同时,再一次证明了Java有多慢,C++肯定不用加最后的剪枝就过了,但是Java就不行
而且有一个点居然要1.5秒,真是不可思议。
待会儿我再试试Analysis里面的代码,看看有多快,是不是Static的要比正常的写快。
算法没啥好说的,就是生成,把F+R个数生成出来,然后用那个最大ratio是最小ratio的三倍做一个剪枝
我之前还打了一个表,所有的数的ratio的表,然后后面直接访问这个表,而不是去现去除
但是发现好像左右不大。
剩下的就是那个Hash的优化了,很强大,就好象题目里说的那样,就是看F生成好之后的比率
比如1 3 5 7,前两个是F的,后两个是R的,那么它的variance和2 6 10 14是一样的,这个不用我解释吧
这样的话呢,同一个比率的就不用再做第二次了。
自己没有去严格的证明这个剪枝的正确性,但是想想证明应该不太难。
同一个ratio的话肯定取前面那组,如果是不同的ratio呢?
现在的问题就是,如果是不同的ratio,后面的正确的组会不会被前面的不小心剪掉。或者后面的这组根本没必要。
如果是根本没必要的话,那么这个剪枝肯定就是正确的了。
其实这个剪枝是正确的,因为在你用比如1,3的时候,你就试过了后面所有的不同组合了。
那么当你扩大1,3到2,6时,我们可以看看USACO后面的解释的那个例子来。
39/16 = 2.43750000000000000000
40/16 = 2.50000000000000000000
39/15 = 2.60000000000000000000
40/15 = 2.66666666666666666666
39/14 = 2.78571428571428571428
40/14 = 2.85714285714285714285
39/13 = 3.00000000000000000000
40/13 = 3.07692307692307692307
39/12 = 3.25000000000000000000
40/12 = 3.33333333333333333333
|
就相当于这些ratio全都翻了一倍,那样的话,variance当然不会变
所以个剪枝是正确的。
贴一下代码:
1 import java.io.*;
2 import java.util.*;
3 /*
4 ID: yanglei4
5 LANG: JAVA
6 TASK:cowcycle
7 */
8 public class cowcycle{
9 public double[][] ratio = new double[81][41];
10 int F,R,F1,F2,R1,R2;
11 public int[] s1;
12 public int[] s2;
13 double min = 1000000;
14 static String[] hash;
15 public int[] ans1;
16 public int[] ans2;
17
18 public static boolean contains(String str) {
19 int num = str.hashCode();
20 if(num<0) num = -num;
21 int p = num % hash.length;
22 while(hash[p]!=null)
23 if(str.equals(hash[p])) return true;
24 else if(p==hash.length-1) p=0;
25 else p++;
26 hash[p]=str;
27 return false;
28 }
29
30 public void DFS_F(int step,int start) {
31 if (step == F + 1) {
32 StringBuffer str = new StringBuffer();
33 for(int i = 2;i <= F;i++)
34 str.append((int)(100*(double)s1[i]/s1[1]));
35 if(contains(str.toString())) return;
36
37 DFS_R(1,R1);
38 return;
39 }
40 for (int i = start; i <= F2 - (F - step); i++) {
41 s1[step] = i;
42 DFS_F(step + 1, i + 1);
43 }
44 }
45
46 public void DFS_R(int step,int start) {
47 if (step == R + 1) {
48 if (s1[F] * s2[R] < 3 * s1[1] * s2[1])
49 return;
50 count();
51 return;
52 }
53 for (int i = start; i <= R2 - (R - step); i++) {
54 s2[step] = i;
55 DFS_R(step + 1, i + 1);
56 }
57 }
58
59 public double count() {
60 double[] rate = new double[R * F + 1];
61 double[] diff = new double[R * F + 1];
62 double sum = 0;
63 double sumf = 0;
64 int l = 1;
65 for (int i = 1; i <= F; i++)
66 for (int j = 1; j <= R; j++)
67 rate[l++] = ratio[s1[i]][s2[j]];
68
69 Arrays.sort(rate);
70
71 for (int i = 1; i <= F * R - 1; i++) {
72 diff[i] = rate[i + 1] - rate[i];
73 sum += diff[i];
74 sumf += diff[i] * diff[i];
75 }
76 double avg = sum / (F * R - 1);
77 double vf = sumf - sum * avg;
78 if (vf < min) {
79 min = vf;
80 for (int i = 1; i <= F; i++) ans1[i] = s1[i];
81 for (int i = 1; i <= R; i++) ans2[i] = s2[i];
82 }
83 return 0.0;
84 }
85
86 public void done() throws IOException {
87 for (int i = 25; i <= 80; i++)
88 for (int j = 5; j <= 40; j++)
89 ratio[i][j] = (double)i / j;
90
91 BufferedReader f = new BufferedReader(new FileReader("cowcycle.in"));
92 PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter("cowcycle.out")));
93 StringTokenizer st = new StringTokenizer(f.readLine());
94 F = Integer.parseInt(st.nextToken());
95 R = Integer.parseInt(st.nextToken());
96 st = new StringTokenizer(f.readLine());
97 F1 = Integer.parseInt(st.nextToken());
98 F2 = Integer.parseInt(st.nextToken());
99 R1 = Integer.parseInt(st.nextToken());
100 R2 = Integer.parseInt(st.nextToken());
101 s1 = new int[F + 1];
102 s2 = new int[R + 1];
103 ans1 = new int[F + 1];
104 ans2 = new int[R + 1];
105 hash = new String[50003];
106
107 DFS_F(1,F1);
108
109 for (int i = 1; i <= F - 1; i++)
110 out.print(ans1[i] + " ");
111 out.println(ans1[F]);
112
113 for (int i = 1; i <= R - 1; i++)
114 out.print(ans2[i] + " ");
115 out.println(ans2[R]);
116
117 out.close();
118 }
119
120 public static void main(String[] args) throws IOException {
121 cowcycle t = new cowcycle();
122 t.done();
123 System.exit(0);
124 }
125 }
126
不明白为什么一模一样的算法,Java的结果就是不对呢,很奇怪
又是一个剪枝的题目,我发现剪枝这东西真是需要灵感的
那个不会改变的字符串的剪枝比较容易想,不过Hash的那个剪枝确实是让我爱莫能及啊,然后就参考了Leokan的代码
很神奇的代码,Hash Size只有3W+,然后居然就AC了,我最后就是用他的代码试了一下 -_-!,然后AC的
可是为什么我的Java代码就是过不了呢?很神奇
在第七个点的时候,Hash Size要开到50W才可以出正确的结果,但是这样的话时间会超长,估计有4,5秒的样子
而且我把能加的剪枝全加上了,还是不行……
真是不明白所以啊,不知道自己问题处在哪里了。
太TM诡异了……
http://www.cmykrgb123.cn/blog/string-hash-compare/
常用的字符串Hash函数还有ELFHash,APHash等等,都是十分简单有效的方法。这些函数使用位运算使得每一个字符都对最后的函数值产生影响。另外还有以MD5和SHA1为代表的杂凑函数,这些函数几乎不可能找到碰撞。
常用字符串哈希函数有BKDRHash,APHash,DJBHash,JSHash,RSHash,SDBMHash,PJWHash,ELFHash等等。对于以上几种哈希函数,我对其进行了一个小小的评测。
Hash函数 |
数据1 |
数据2 |
数据3 |
数据4 |
数据1得分 |
数据2得分 |
数据3得分 |
数据4得分 |
平均分 |
BKDRHash |
2 |
0 |
4774 |
481 |
96.55 |
100 |
90.95 |
82.05 |
92.64 |
APHash |
2 |
3 |
4754 |
493 |
96.55 |
88.46 |
100 |
51.28 |
86.28 |
DJBHash |
2 |
2 |
4975 |
474 |
96.55 |
92.31 |
0 |
100 |
83.43 |
JSHash |
1 |
4 |
4761 |
506 |
100 |
84.62 |
96.83 |
17.95 |
81.94 |
RSHash |
1 |
0 |
4861 |
505 |
100 |
100 |
51.58 |
20.51 |
75.96 |
SDBMHash |
3 |
2 |
4849 |
504 |
93.1 |
92.31 |
57.01 |
23.08 |
72.41 |
PJWHash |
30 |
26 |
4878 |
513 |
0 |
0 |
43.89 |
0 |
21.95 |
ELFHash |
30 |
26 |
4878 |
513 |
0 |
0 |
43.89 |
0 |
21.95 |
其中数据1为100000个字母和数字组成的随机串哈希冲突个数。数据2为100000个有意义的英文句子哈希冲突个数。数据3为数据1的哈希值与
1000003(大素数)求模后存储到线性表中冲突的个数。数据4为数据1的哈希值与10000019(更大素数)求模后存储到线性表中冲突的个数。
经过比较,得出以上平均得分。平均数为平方平均数。可以发现,BKDRHash无论是在实际效果还是编码实现中,效果都是最突出的。APHash也
是较为优秀的算法。DJBHash,JSHash,RSHash与SDBMHash各有千秋。PJWHash与ELFHash效果最差,但得分相似,其算
法本质是相似的。
在信息修竞赛中,要本着易于编码调试的原则,个人认为BKDRHash是最适合记忆和使用的。。
附:各种哈希函数的C语言程序代码
unsigned int SDBMHash(char *str)
{
unsigned int hash = 0;
while (*str)
{
// equivalent to: hash = 65599*hash + (*str++);
hash = (*str++) + (hash << 6) + (hash << 16) - hash;
}
return (hash & 0x7FFFFFFF);
}
// RS Hash Function
unsigned int RSHash(char *str)
{
unsigned int b = 378551;
unsigned int a = 63689;
unsigned int hash = 0;
while (*str)
{
hash = hash * a + (*str++);
a *= b;
}
return (hash & 0x7FFFFFFF);
}
// JS Hash Function
unsigned int JSHash(char *str)
{
unsigned int hash = 1315423911;
while (*str)
{
hash ^= ((hash << 5) + (*str++) + (hash >> 2));
}
return (hash & 0x7FFFFFFF);
}
// P. J. Weinberger Hash Function
unsigned int PJWHash(char *str)
{
unsigned int BitsInUnignedInt = (unsigned int)(sizeof(unsigned int) * 8);
unsigned int ThreeQuarters = (unsigned int)((BitsInUnignedInt * 3) / 4);
unsigned int OneEighth = (unsigned int)(BitsInUnignedInt / 8);
unsigned int HighBits = (unsigned int)(0xFFFFFFFF) << (BitsInUnignedInt - OneEighth);
unsigned int hash = 0;
unsigned int test = 0;
while (*str)
{
hash = (hash << OneEighth) + (*str++);
if ((test = hash & HighBits) != 0)
{
hash = ((hash ^ (test >> ThreeQuarters)) & (~HighBits));
}
}
return (hash & 0x7FFFFFFF);
}
// ELF Hash Function
unsigned int ELFHash(char *str)
{
unsigned int hash = 0;
unsigned int x = 0;
while (*str)
{
hash = (hash << 4) + (*str++);
if ((x = hash & 0xF0000000L) != 0)
{
hash ^= (x >> 24);
hash &= ~x;
}
}
return (hash & 0x7FFFFFFF);
}
// BKDR Hash Function
unsigned int BKDRHash(char *str)
{
unsigned int seed = 131; // 31 131 1313 13131 131313 etc..
unsigned int hash = 0;
while (*str)
{
hash = hash * seed + (*str++);
}
return (hash & 0x7FFFFFFF);
}
// DJB Hash Function
unsigned int DJBHash(char *str)
{
unsigned int hash = 5381;
while (*str)
{
hash += (hash << 5) + (*str++);
}
return (hash & 0x7FFFFFFF);
}
// AP Hash Function
unsigned int APHash(char *str)
{
unsigned int hash = 0;
int i;
for (i=0; *str; i++)
{
if ((i & 1) == 0)
{
hash ^= ((hash << 7) ^ (*str++) ^ (hash >> 3));
}
else
{
hash ^= (~((hash << 11) ^ (*str++) ^ (hash >> 5)));
}
}
return (hash & 0x7FFFFFFF);
}
今天在Lab想连一下MySQL的数据库,因为电脑不知道什么时候被人关掉了
今天又被我开起来了,之前连的都好好的,我也没做过什么特殊的处理
大概就是刚刚装好的时候,security的那个check一直过不了,然后我就搞来搞去弄了什么权限的东西
我自己都记不得是什么指令了,但是其实还是我对数据库这东西不熟悉,只懂一点点的简单的SQL
然后今天就发现连不上了,Error Number 1130,于是我去Google一下,发现这个是远程root用户没有权限的问题
原来root用户只有本地的权限,需要手动将远程的权限打开,尝试了好几种方法,最后还是下面这种方法管用
在安装mysql的机器上运行:
1、d:"mysql"bin">mysql -h localhost -u root
//这样应该可以进入MySQL服务器
2、mysql>GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION
//赋予任何主机访问数据的权限
3、mysql>FLUSH PRIVILEGES
//修改生效
4、mysql>EXIT
//退出MySQL服务器 |
想懂很多东西。
Fence Rails
一道感觉很不错的搜索+剪枝的题目,当然剪枝策略我是参考来的,我自己想到的都是小剪枝
然后我自己用Java实现了,结果发现……,这速度啊,真让我郁闷,居然在第四个点就超时了
但是同样的算法用C++实现的代码,快了100+倍。
难怪现在还有这么多人在搞C++,也难怪有人批评说Java速度慢,还有些人在反驳
事实却是如此啊。
代码贴一下吧,TLE的,如果有人愿意的话,可以转成C++用就行了
其实我是想能不能再改进改进,让它用Java也能过,但是感觉很难,我已经想不出更好的剪枝的办法了
或者另外一个方法就是空间换时间,但是感觉已经没什么好换的了,该开的数组都开了。
1 import java.io.*;
2 import java.util.*;
3 /*
4 ID: yanglei4
5 LANG: JAVA
6 TASK:fence8
7 */
8 public class fence8{
9 int wastemax = 0;
10 int ans;
11 int N,R;
12 int[] remain;
13 int[] rails;
14 int[] from = new int[1100];
15 PrintWriter out;
16
17 public void dfs(int k, int waste) {
18 if (k < 0) {
19 out.println(ans + 1);
20 out.close();
21 System.exit(0);
22 }
23 int i;
24 if(k != ans && rails[k] == rails[k + 1]) i = from[k + 1];
25 else i = 0;
26
27 for (i = 0; i < N; i++) {
28 if (remain[i] >= rails[k]) {
29
30 from[k] = i;
31 remain[i] -= rails[k];
32 if (remain[i] < rails[0]) waste += remain[i];
33 if (waste > wastemax) {
34 waste -= remain[i];
35 remain[i] += rails[k];
36 continue;
37 }
38 dfs(k - 1, waste);
39 remain[i] += rails[k];
40 }
41 }
42 }
43
44 public void done() throws IOException {
45 BufferedReader f = new BufferedReader(new FileReader("fence8.in"));
46 out = new PrintWriter(new BufferedWriter(new FileWriter("fence8.out")));
47 //Read the boards
48 N = Integer.parseInt(f.readLine());
49 int[] boards = new int[N];
50 int boardSum = 0;
51 for (int i = 0; i < N; i++)
52 {
53 boards[i] = Integer.parseInt(f.readLine());
54 boardSum += boards[i];
55 }
56 Arrays.sort(boards);
57
58 remain = new int[N];
59 for (int i = N - 1; i >= 0; i--) remain[i] = boards[i];
60
61 // Read the rails
62 R = Integer.parseInt(f.readLine());
63 rails = new int[R];
64 int railSum = 0;
65 for (int i = 0; i < R; i++)
66 rails[i] = Integer.parseInt(f.readLine());
67 Arrays.sort(rails);
68
69 int i;
70 for (i = 0; i < R; i++) {
71 if (railSum + rails[i] > boardSum)
72 break;
73 railSum += rails[i];
74 }
75 int k = i - 1;
76 //Try every possible number
77 for (i = k; k >= 0; i--) {
78 ans = i;
79 wastemax = boardSum - railSum;
80 railSum -= rails[i];
81 dfs(i,0);
82 }
83 out.println(0);
84 out.close();
85 }
86 public static void main(String[] args) throws IOException {
87 fence8 t = new fence8();
88 t.done();
89 System.exit(0);
90 }
91 }
92
|
|
随笔:99
文章:-1
评论:17
引用:0
留言簿(1)
随笔分类
随笔档案
相册
搜索
最新评论
|
|