Yacc 與 Lex 練習 - UVa 11291

Problem

詳細題目請參考 UVa 11291 - Smeech

語法

1
2
<E[x]> -> <Integer> | (p <E[x]> <E[x]>)
<Integer> -> #number

目標計算出 E[X] = p * (e1 + e2) + (1 - p) * (e1 - e2)

Yacc

Yacc 主要是根據 CFG 進行解析。

其中,要對於 Yacc 語法的回傳值明白操作情況。

對於語法回傳值,我們來個最簡單的範例:

1
2
expression -> LEFT_P NUMBER expression expression RIGHT_P
$$ $1 $2 $3 $4 $5

其中 $$ 是回傳值,其中的 $1$2 … 等,都是得到的回傳值。

對於 yylval 形態處理:

1
2
3
4
%union {
float floatVal;
int intVal;
}

通常使用 union 的方式來共用記憶體,在 lex 那邊就可以根據 yylval.floatVal 進行操作。

當然,我們也要對於每一個 nonterminal 定義回傳的型態

1
2
3
%type <floatVal> NUMBER
%type <floatVal> expression
%type <floatVal> startsymbol

基本上就大功告成了。

接著要考慮如何去完成多組測資組的問題,在這裡可以利用 kleene star 來完成。

但是對於輸出順序,使用 input -> input startsymbolinput -> startsymbol input 輸出順序是不同的,如果要順著輸出,請選擇前者。

file: a.y
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
%{
#include <stdio.h>
int yylex();
double ans = 0;
void yyerror(const char* message) {
ans = -2147483647;
printf("Invaild format\n");
};
%}
%union {
float floatVal;
int intVal;
}
%type <floatVal> NUMBER
%type <floatVal> expression
%type <floatVal> startsymbol
%token LEFT_P RIGHT_P NUMBER
%%
input
: input startsymbol {
ans = $2;
printf("%.2f\n", ans);
}
| {
}
startsymbol
: expression {
$$ = $1;
}
;
expression
: NUMBER {
$$ = $1;
}
| LEFT_P NUMBER expression expression RIGHT_P {
$$ = $2 * ($3 + $4) + (1.0f - $2) * ($3 - $4);
}
;
%%
int main() {
yyparse();
return 0;
}

Lex

Lex 主要是扮演切出所有的 token 的角色,相當於 Scanner。同時要將值紀錄再 yylval 中,在 Yacc 那邊則會在 $NUMBER 中得到變數值。

在 Lex 語法上要特別小心,如果兩條的前綴 (prefix) 相同時 (解析上會產生相同的 token),則會選擇長的那一條為優先。如果長度仍相同的話,請自行測試,這裡無法給予解答。而為什麼要這麼設計?為了保證後綴不會塞入奇怪的字符,因此可以先檢查隨後接的字符情況。

file: a.l
1
2
3
4
5
6
7
8
9
10
11
12
13
%{
#include "a.tab.h"
#include <stdio.h>
%}
%%
([-]?)[0-9]*\.[0-9]* {sscanf(yytext, "%f", &yylval.floatVal); return NUMBER;}
([-]?)[0-9]+ {sscanf(yytext, "%f", &yylval.floatVal); return NUMBER;}
"(" {return LEFT_P;}
")" {return RIGHT_P;}
\n {}
. {}
%%

make file

請用以下方法建造

1
morris $ sh make.sh

代碼如下

file: make.sh
1
2
3
4
5
bison -d a.y
flex a.l
gcc -c a.tab.c
gcc -c lex.yy.c
gcc lex.yy.o a.tab.o -lfl

產生的結果如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ tree
. ├── a.l
├── a.out
├── a.tab.c
├── a.tab.h
├── a.tab.o
├── a.y
├── input~
├── lex.yy.c
├── lex.yy.o
├── make.sh
├── make.sh~
├── testdata
│ ├── ac_output
│ └── input

執行

執行的時候就跟標準輸入串流一樣。

1
morris $ ./a.out <input

參考範例輸入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
7
(.5 3 9)
(0.3 2 (1 1 -10))
(1.0 (1.0 (1.0 -1 -2) (1.0 -1 -2)) (1.0 (1.0 1 2) 2))
(0.99 82 (0.76 50 (0.85 (0.07 (0.88 28 (0.98 79 (0.09 77 51))) (0.30 (0.29 36 (0.36 (0.51 27 (0.68 (0.47 87 35) (0.25 85 59))) 31)) 64)) (0.14 (0.84 (0.77 29 71) (0.50 (0.99 (0.81 (0.78 (0.87 50 7) 71) 72) 14) (0.30 (0.50 9 37) (0.67 21 (0.73 86 (0.08 31 14)))))) (0.94 (0.96 (0.50 27 (0.94 (0.54 62 (0.63 89 29)) (0.81 24 (0.94 20 72)))) 4) (0.44 9 (0.59 (0.70 48 40) (0.64 (0.27 97 71) 43))))))))
(0.15 12 60)
(0.55 18 (0.95 (0.79 95 (0.97 (0.11 (0.96 (0.70 (0.38 73 66) (0.38 (0.06 (0.02 88 66) (0.39 35 37)) 69)) (0.81 3 (0.45 5 61))) (0.44 (0.67 (0.23 82 7) 38) (0.24 49 74))) 76)) (0.46 19 44)))
(0.56 62 77)
(0.03 (0.90 19 35) (0.17 50 (0.13 (0.96 74 (0.07 (0.51 78 (0.77 (0.78 (0.84 64 (0.41 7 59)) 21) (0.73 (0.95 (0.13 2 85) (0.08 29 50)) (0.24 (0.12 43 99) 71)))) 84)) (0.31 69 (0.82 31 24)))))
(0.78 73 44)
(0.09 (0.80 14 62) (0.57 (0.57 95 (0.33 94 31)) (0.70 (0.92 32 (0.13 20 (0.44 87 (0.45 83 26)))) (0.43 40 (0.51 (0.57 29 (0.65 13 (0.40 74 56))) 56)))))
(0.96 (0.96 (0.65 (0.01 (0.98 (0.34 86 (0.68 87 21)) 27) (0.26 68 68)) (0.42 28 29)) 9) 99)
(0.99 (0.60 53 27) (0.35 23 (0.15 (0.50 85 (0.73 56 (0.34 (0.44 (0.10 (0.62 (0.20 58 15) 56) (0.57 74 (0.23 20 78))) 62) (0.92 (0.72 (0.82 75 (0.08 35 93)) 43) (0.55 10 (0.45 (0.25 52 91) 71)))))) (0.61 39 (0.49 (0.92 87 (0.13 52 (0.69 (0.46 (0.82 27 72) 96) (0.27 51 99)))) (0.36 99 (0.60 68 (0.49 (0.25 (0.92 54 7) (0.48 47 32)) (0.08 (0.69 97 11) 34)))))))))
(0.87 (0.32 (0.42 (0.66 69 (0.98 0 (0.46 94 (0.25 (0.57 (0.72 25 59) (0.35 91 (0.42 29 28))) 3)))) 19) 81) (0.72 (0.06 (0.66 (0.07 22 87) 44) (0.18 33 14)) 49))
(0.72 (0.01 67 (0.13 (0.22 (0.88 11 86) (0.04 40 (0.41 (0.86 (0.81 (0.08 84 95) (0.88 19 67)) 8) 96))) 13)) (0.13 45 (0.19 (0.83 85 (0.77 (0.37 78 (0.44 44 59)) 37)) 56)))
(0.51 (0.36 2 72) 25)
(0.32 51 87)
(0.06 (0.23 (0.70 93 47) (0.22 20 (0.90 (0.03 (0.48 (0.84 18 (0.29 (0.02 (0.06 52 49) 67) (0.49 (0.69 60 74) (0.59 92 0)))) 32) 80) (0.76 78 (0.66 8 94))))) 31)
(0.29 (0.54 70 (0.64 (0.48 (0.50 25 (0.38 (0.07 (0.14 (0.44 (0.76 44 45) 67) 41) (0.15 (0.86 (0.89 87 42) (0.43 85 58)) (0.98 44 (0.48 34 28)))) (0.84 81 60))) 28) 16)) (0.84 (0.21 34 (0.78 (0.76 (0.51 82 (0.07 (0.78 (0.52 (0.65 51 58) 12) (0.05 52 (0.98 0 30))) (0.01 27 3))) (1.00 15 (0.53 (0.91 (0.38 62 (0.75 92 69)) (0.85 53 (0.84 92 83))) (0.70 (0.12 (0.24 23 83) 58) (0.62 56 38))))) 36)) (0.66 41 13)))
(0.94 (0.47 30 56) (0.78 6 (0.45 (0.45 15 94) (0.77 (0.57 25 (0.33 66 12)) (0.66 74 (0.82 (0.14 75 50) (0.22 45 43)))))))
(0.66 (0.58 (0.85 75 26) 45) (0.04 (0.94 82 (0.59 34 (0.02 (0.09 11 (0.31 (0.05 (0.92 (0.14 51 45) (0.47 25 46)) 6) 5)) 33))) (0.81 (0.09 15 (0.91 (0.77 (0.59 44 (0.57 (0.39 (0.81 18 75) (0.37 41 67)) 18)) (0.77 (0.68 (0.71 (0.31 87 94) 85) (0.00 35 (0.55 81 22))) 56)) 3)) (0.43 7 (0.98 94 3)))))
(0.13 22 80)
(0.77 45 81)
(0.61 (0.21 (0.32 (0.32 (0.81 8 60) 78) (0.19 (0.92 68 (0.56 (0.22 79 32) (0.32 72 63))) 41)) (0.30 94 31)) (0.80 59 33))

參考範例輸出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
7.00
3.00
5.60
-1.00
241.55
-30.00
32.05
71.24
25.80
97.64
-37.98
153.38
67.92
35.81
-10.21
-17.66
19.68
60.84
92.20
30.61
157.62
-37.20
88.74
-48.46

Reference

Yacc 与 Lex 快速入门

指導

Inker

Read More +

UVa 1636 - Headshot

Problem

You have a revolver gun with a cylinder that has n chambers. Chambers are located in a circle on a cylinder. Each chamber can be empty or can contain a round. One chamber is aligned with the gun’s barrel. When trigger of the gun is pulled, the gun’s cylinder rotates, aligning the next chamber with the barrel, hammer strikes the round, making a shot by firing a bullet through the barrel. If the chamber is empty when the hammer strikes it, then there is no shot but just a click.

You have found a use for this gun. You are playing Russian Roulette with your friend. Your friend loads rounds into some chambers, randomly rotates the cylinder, aligning a random chamber with a gun’s barrel, puts the gun to his head and pulls the trigger. You hear click and nothing else – the chamber was empty and the gun did not shoot.

Now it is your turn to put the gun to your head and pull the trigger. You have a choice. You can either pull the trigger right away or you can randomly rotate the gun’s cylinder and then pull the trigger. What should you choose to maximize the chances of your survival?

Input

The input file contains several datasets. A dataset contains a single line with a string of n digits 0 and 1 ( 2 <= n <= 100). This line of digits represents the pattern of rounds that were loaded into the gun’s chambers. 0 represent an empty chamber, 1 represent a loaded one. In this representation, when cylinder rotates before a shot, the next chamber to the right gets aligned with the barrel for a shot. Since the chambers are actually located on a circle, the first chamber in this string follows the last one. There is at least one 0 in this string.

Output

For each dataset, write to the output file one of the following words (without quotes):

  • SHOOT – if pulling the trigger right away makes you less likely to be actually shot in the head with the bullet (more likely that the chamber will be empty).
  • ROTATE – if randomly rotating the cylinder before pulling the trigger makes you less likely to be actually shot in the head with the bullet (more likely that the chamber will be empty).
  • EQUAL – if both of the above choices are equal in terms of probability of being shot.

Sample Input

1
2
3
0011
0111
000111

Sample Output

1
2
3
EQUAL
ROTATE
SHOOT

Solution

題目描述:

俄羅斯輪盤,現在與一位朋友一起玩這場遊戲,然而我們已經知道左輪手槍中的填彈資訊,剛聽到朋友按下扣板,但是沒有死。現在輪到你,請問要直接按?還是隨機轉一圈再按?

題目解法:

不外乎地,我們要找到哪個機率高。

由於朋友按下沒死,也就是要考慮連續 2 個 0 的情況,下一個是 0 的機率為 czero / zero

// 在前提為 0 的情況下,下一個為 0 的機率為何,也就是考慮 0001 的情況下,00 的機率為何。
// czero 是連續為 0 的個數,zero 是 0 的個數 (同時也是 00 01 的個數。),n 是全部個數。

隨機轉一圈得到 0 的概率為 zero / n

不小心把題目想太難,事實上這問題很簡單的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdio.h>
#include <string.h>
int main(){
char s[1024];
while(scanf("%s", s) == 1){
int n = strlen(s);
int zero = 0, one = 0, czero = 0;
for(int i = 0 ; i < n ; i++ ){
if(s[i] == '1')
continue;
zero++;
if(s[(i+1)%n] == '0') czero++;
}
// czero - zero * (zero / n)
if(czero * n == zero * zero)
puts("EQUAL");
else if(czero * n > zero * zero)
puts("SHOOT");
else
puts("ROTATE");
}
return 0;
}
Read More +

UVa 1644 - Prime Gap

Problem

The sequence of n - 1 consecutive composite numbers (positive integers that are not prime and not equal to 1) lying between two successive prime numbers p and p + n is called a prime gap of length n . For example, 〈24, 25, 26, 27, 28〉 between 23 and 29 is a prime gap of length 6.

Your mission is to write a program to calculate, for a given positive integer k , the length of the prime gap that contains k . For convenience, the length is considered 0 in case no prime gap contains k .

Input

The input is a sequence of lines each of which contains a single positive integer. Each positive integer is greater than 1 and less than or equal to the 100000th prime number, which is 1299709. The end of the input is indicated by a line containing a single zero.

Output

The output should be composed of lines each of which contains a single non-negative integer. It is the length of the prime gap that contains the corresponding positive integer in the input if it is a composite number, or `0’ otherwise. No other characters should occur in the output.

Sample Input

1
2
3
4
5
6
10
11
27
2
492170
0

Sample Output

1
2
3
4
5
4
0
6
0
114

Solution

題目描述:

找該數字左右相鄰的兩個質數差值。

題目解法:

線性篩法 (sieve)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <stdio.h>
#include <stdlib.h>
#define LL long long
#define maxL (1300000>>5)+1
#define GET(x) (mark[x>>5]>>(x&31)&1)
#define SET(x) (mark[x>>5] |= 1<<(x&31))
using namespace std;
int mark[maxL];
int anaP[30], at = 0;
void sieve() {
register int i, j, k;
SET(1);
int n = 1300000;
for(i = 2; i < n; i++) {
if(!GET(i)) {
for(k = n/i, j = i*k; k >= i; k--, j -= i)
SET(j);
}
}
}
main() {
sieve();
int n;
while(scanf("%d", &n) == 1 && n) {
int ret = 0;
for(int i = n; GET(i); i++, ret++);
for(int i = n; GET(i) && i > 0; i--, ret++);
printf("%d\n", ret);
}
return 0;
}
Read More +

編譯器 Compiler CFG LL(1)

序文

相較於其他技術而言,編譯器的確不是什麼有趣的課程,在這一門領域專研的人也變少了,所以專精的人越少,將會越有機會。易見地,也越來越少大學開設這門編譯器的課程,不過在一些公司,編譯器的技術將成為私囊秘寶,如何將代碼更加地優化快速,這就是相當令人感到為之一驚的地方。

DFA, NFA, Regular expression

在討論所有語法前,要明白正規表達式 ( Regular expression = RegExp ) 的運作,RegExp 運作上涵蓋的語法集合是最小的,對於一個 RegExp 來說,可以轉換成具有 n 個狀態的 NFA,而這台 NFA 可以轉換成 2n 狀態的 DFA。

NFA 和 DFA 最大的不同在於轉移狀態時,考慮的路徑情況,NFA 可以在不考慮輸入進行轉移,或者在相同情況下可以有很多轉移選擇。 DFA 則必須在一個輸入唯一一個轉移情況下運作。

如上圖 DFA 所示,邊上的內容就是根據輸入的情況進行轉移。

如上圖 NFA 所示,會發現狀態 p 對於輸入 1 有 2 種轉移可能 (p->p or p->q)。

最後可以得到一個 RegExp 一定會有一台 DFA 辨識它。而一台 DFA 也一定會有一個 RegExp,這句話的含意將可以在 計算理論 這門課中學到如何藉由類似 Floyd Algorithm 內點性質依序推出 DFA 對應的 RegExp。

CFG (Context-free grammar)

A context-free grammar is a formal system that describes a language by specifying how any legal string (i.e., sequence of tokens) can be derived from a start symbol. It consists of a set of productions, each of which states that a given symbol can be replaced by a given sequence of symbols.

另一個類似的語言是 Context-sensitive grammar,等價性質的兄弟是 Chomsky normal form

  • CFG G = (Vt, Vn, S, P)

    • Vt: 有限的 terminal 字符集
    • Vn: 有限的 nonterminal 字符集
    • S: 一開始出發的 start symbol
    • P: 轉換的規則 (production)
    • 來個例子

      LHS (left-hand side) -> RHS (right-hand side)
      
      <E>         -> <Prefix> ( <E> )
      <E>         -> V <Tail>
      <Prefix>    -> F
      <Prefix>    -> l
      <Tail>      -> + <E>
      <Tail>      -> l
      

      明顯地

      • Vt = {(, ), V, F, l, +}
      • Vn = {<E>, <Prefix>, <Tail>}
      • S = <E>
      • P 就是給定那六條規規則
  • 名詞
    • Sentential form
      If S =>* β, β then is said to be sentential form of the CFG
      簡單來說,所有從 start symbol 過程中替換出來的句子都稱為 sentential form。
    • Handle of a sentential form
      The handle of a sentential form is the left-most simple phrase.
      img
      簡單來說,就是將一個 nonterminal 根據 production 轉換成 RHS (right-hand side) 的一個步驟。
  • 什麼是 CFG (上下文無關語法)?
    簡單來說,還真的是上下文無關,例如 S -> AB 可以見到 S 轉換時,不會因為前文或後文是什麼內容而影響,而在 Context-sensitive grammar (上下文敏感) 可以見到 aSb -> AB 會因為前面跟後面所受到的影響。

這裡使用大寫為 nontermainl,其他字符為 terminal。

First Set

  • First(A)
    • The set of all the terminal symbols that can begin a sentential form derivable from A. A =>* aB
      First(A) = {
          a in Vt | A =>* aB, and
          Λ in Vt | A =>* Λ
      }
      
    • 簡單地說,從這個 nonterminal 開始,第一個不能被替換的 terminal 就會在 First set 中。
  • 計算 First(A) 前,必須先建出這個 nonterminal 有沒有可能推出 Λ,即 A =>* Λ
    這邊相當有意思,使用迭代更新,如果發現 B =>* ΛC =>* Λ 則對於 Production A => BC 來說,可以得到 A =>* Λ 就這麼依序推出所有情況,時間複雜度應該在 O(n^2)
  • 計算 First(A) 時,掃描規則 LHS => RHS,找到 First(RHS) 並且 First(LHS).insert(First(RHS)),依樣使用迭代更新推出所有情況,時間複雜度應該在 O(n^2)

Follow Set

  • Follow(A)
    • A is any nonterminal
    • Follow(A) is the set of terminals that may follow A in some sentential form. S =>* ... Aabc ...follow set。
      Follow(A) = {
          a in Vt | S =>* ... Aa ..., and
          Λ in Vt | S =>* aA
      }
      
    • 請切記,一定要從 start symbol 開始替換的所有句子,而在這個 nonterminal 隨後可能跟著的 termainl 就會屬於它的。
  • 計算 Follow(A) 前,請先把 First set 都算完,接著也是用迭代更新,掃描規則 A -> a B b
    對於
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    if Λ not in First(b)
    Follow(B).insert(First(b))
    else
    Follow(B).insert(First(b) - Λ)
    Follow(B).insert(Follow(A))
    ```
    ## Predict Set ##
    * Given the productions
    * During a (leftmost) derivation, Deciding which production to match, Using lookahead symbols
    * 相較於 First set 和 Follow set,Predict Set 是根據 Production 產生的,也就是說一條 production 會有一個對應的 predict set,而 First set 和 Follow set 都是根據 nonterminal 產生一對一對應的集合,用來預測這一條規則的第一個 terminal 會有哪些。
    對於 production : `A -> X1 X2 ... Xm`

if Λ in First(X1 X2 … Xm)
Predict(A -> X1 X2 … Xm).insert(First(X1 X2 … Xm) - Λ);
Predict(A -> X1 X2 … Xm).insert(Follow(A));
else
Predict(A -> X1 X2 … Xm).insert(First(X1 X2 … Xm));

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
對於第一判斷情況在于 `... AE = ... X1 X2 ... Xm E`,明顯地可以從 `Follow(A)` 得到第一個字符為何。
## LL(1) Table ##
> 尚未撰寫
C++ code
=====
## 資料規格 ##
以下代碼實作 CFG 的 First Set, Follow Set, Predict Set 和 LL(1) Table。
用字符 `l` 代替 lambda
## 資料範例 ##
* 本課程簡易範例輸入

S->aSe
S->B
B->bBe
B->C
C->cCe
C->d

A->BaAb
A->cC
B->d
B->l
C->eA
C->l

A->aAd
A->B
B->bBd
B->C
C->cCd
C->l

E->TX
X->+E
X->#
T->(E)
T->intY
Y->*T
Y->#

E->P(E)
E->vT
P->f
P->l
T->+E
T->l

S->aSe
S->B
B->bBe
B->C
C->cCe
C->d

S->ABc
A->a
A->l
B->b
B->l

1
2
* 本課程簡易範例輸出

B:b,c,d
C:c,d
S:a,b,c,d
a:a
b:b
c:c
d:d
e:e
A:a,c,d
B:d,l
C:e,l
a:a
b:b
c:c
d:d
e:e
A:a,b,c,l
B:b,c,l
C:c,l
a:a
b:b
c:c
d:d

#:#
(:(
):)
:
+:+
E:(,i
T:(,i
X:#,+
Y:#,*
i:i
n:n
t:t
(:(
):)
+:+
E:(,f,v
P:f,l
T:+,l
f:f
v:v
B:b,c,d
C:c,d
S:a,b,c,d
a:a
b:b
c:c
d:d
e:e
A:a,l
B:b,l
S:a,b,c
a:a
b:b
c:c

1
2
3
4
- - - - -
* HTML 格式範例輸入

-> begin end
->
->
-> l
-> ID := ;
-> read ( ) ;
-> write ( ) ;
-> ID
-> , ID
-> l
->
-> ,
-> l
->
->
-> l
-> ( )
-> ID
-> INTLIT
-> +
-> -
-> $

begin ID := ID - INTLIT + ID ; end $

1
* HTML 格式範例輸出

+—————-+—– First set —–+
|$ | { $ }
|( | { ( }
|) | { ) }
|+ | { + }
|, | { , }
|- | { - }
|:= | { := }
|; | { ; }
| | { + - }
| | { ( ID INTLIT }
| | { , l }
| | { ( ID INTLIT }
| | { ID }
| | { , l }
| | { ( ID INTLIT }
| | { + - l }
| | { begin }
| | { ID read write }
|| { ID read write }
|| { ID l read write }
| | { begin }
|ID | { ID }
|INTLIT | { INTLIT }
|begin | { begin }
|end | { end }
|read | { read }
|write | { write }
+—————-+———————+

+—————-+—– Follow set —–+
| | { ( ID INTLIT }
| | { ) }
| | { ) }
| | { ) , ; }
| | { ) }
| | { ) }
| | { ) + , - ; }
| | { ) , ; }
| | { $ }
| | { ID end read write }
|| { end }
|| { end }
| | { l }
+—————-+———————+

+—————-+—– LL(1) table —–+
| | $| (| )| +| ,| -| :=| ;| ID|INTLIT| begin| end| read| write|
| | | | | 20| | 21| | | | | | | | |
| | | 11| | | | | | | 11| 11| | | | |
| | | | 13| | 12| | | | | | | | | |
| | | 14| | | | | | | 14| 14| | | | |
| | | | | | | | | | 8| | | | | |
| | | | 10| | 9| | | | | | | | | |
| | | 17| | | | | | | 18| 19| | | | |
| | | | 16| 15| 16| 15| | 16| | | | | | |
| | | | | | | | | | | | 1| | | |
| | | | | | | | | | 5| | | | 6| 7|
|| | | | | | | | | 2| | | | 2| 2|
|| | | | | | | | | 3| | | 4| 3| 3|
| | | | | | | | | | | | 22| | | |
+—————-+———————+

Process syntax accept

```

代碼

2014/6/1 修正 parsing 時的 lambda 問題。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
#include <stdio.h>
#include <vector>
#include <iostream>
#include <sstream>
#include <string>
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <ctype.h>
#include <assert.h>
using namespace std;
#define HTMLProduction
class Production {
public:
string LHS;
vector<string> RHS;
int label;
Production(string L = "", vector<string> R = vector<string>()) {
LHS = L;
RHS = R;
}
void print() {
printf("%s -> ", LHS.c_str());
for(size_t i = 0; i < RHS.size(); i++)
printf("%s", RHS[i].c_str());
}
};
class Grammar {
public:
static const string lambda;
string start_symbol;
vector<Production> rules;
map<string, set<string> > first_set;
map<string, set<string> > follow_set;
map<string, bool> derives_lambda;
map<string, map<string, Production> > lltable;
map<string, bool> mark_lambda();
void fill_first_set();
void fill_follow_set();
bool isNonterminal(string token);
set<string> compute_first(vector<string> rhs);
set<string> get_predict_set(Production p);
void fill_lltable();
void lldriver(queue<string> tokens);
};
const string Grammar::lambda("l");
set<string> Grammar::compute_first(vector<string> rhs) {
set<string> result;
size_t i;
if(rhs.size() == 0 || rhs[0] == Grammar::lambda) {
result.insert(Grammar::lambda);
} else {
result = first_set[rhs[0]];
for(i = 1; i < rhs.size() &&
first_set[rhs[i-1]].find(Grammar::lambda) != first_set[rhs[i-1]].end(); i++) {
set<string> f = first_set[rhs[i]];
f.erase(Grammar::lambda);
result.insert(f.begin(), f.end());
}
if(i == rhs.size()
&& first_set[rhs[i-1]].find(Grammar::lambda) != first_set[rhs[i-1]].end()) {
result.insert(Grammar::lambda);
} else {
result.erase(Grammar::lambda);
}
}
return result;
}
/*
* please call get_predict_set() after fill_follow_set() and fill_first_set()
*/
set<string> Grammar::get_predict_set(Production p) {
set<string> result;
set<string> rfirst;
rfirst = compute_first(p.RHS);
if(rfirst.find(Grammar::lambda) != rfirst.end()) {
rfirst.erase(Grammar::lambda);
result.insert(rfirst.begin(), rfirst.end());
rfirst = follow_set[p.LHS];
result.insert(rfirst.begin(), rfirst.end());
} else {
result.insert(rfirst.begin(), rfirst.end());
}
return result;
}
/*
*
*/
void Grammar::fill_lltable() {
string A; // nonterminal
Production p;
for(size_t i = 0; i < rules.size(); i++) {
p = rules[i];
A = p.LHS;
set<string> predict_set = get_predict_set(p);
for(set<string>::iterator it = predict_set.begin();
it != predict_set.end(); it++) {
assert(lltable[A].find(*it) == lltable[A].end());
lltable[A][*it] = p;
}
}
}
bool Grammar::isNonterminal(string token) {
#ifdef HTMLProduction
if(token == Grammar::lambda)
return false;
if(token[0] == '<' && token[token.length() - 1] == '>')
return true;
return false;
#else
if(token == Grammar::lambda)
return false;
for(size_t i = 0; i < token.length(); i++) {
if(isupper(token[i]))
return true;
}
return false;
#endif
}
map<string, bool> Grammar::mark_lambda() {
bool rhs_derives_lambda;
bool changes;
Production p;
derives_lambda.clear();
/* initially, nothing is marked. */
for(size_t i = 0; i < rules.size(); i++) {
derives_lambda[rules[i].LHS] = false;
}
do {
changes = false;
for(size_t i = 0; i < rules.size(); i++) {
p = rules[i];
if(!derives_lambda[p.LHS]) {
if(p.RHS.size() == 0 || p.RHS[0] == Grammar::lambda) {
changes = derives_lambda[p.LHS] = true;
continue;
}
/* does each part of RHS derive lambda ? */
rhs_derives_lambda = derives_lambda[string(p.RHS[0])];
for(size_t j = 1; j < p.RHS.size(); j++) {
rhs_derives_lambda &= derives_lambda[p.RHS[j]];
}
if(rhs_derives_lambda) {
changes = true;
derives_lambda[p.LHS] = true;
}
}
}
} while(changes);
return derives_lambda;
}
void Grammar::fill_first_set() {
string A; // nonterminal
string a; // terminal
Production p;
bool changes;
mark_lambda();
first_set.clear();
for(size_t i = 0; i < rules.size(); i++) {
A = rules[i].LHS;
if(derives_lambda[A])
first_set[A].insert(Grammar::lambda);
}
for(size_t i = 0; i < rules.size(); i++) {
for(size_t j = 0; j < rules[i].RHS.size(); j++) {
a = rules[i].RHS[j];
if(!isNonterminal(a)) {
if(a != Grammar::lambda)
first_set[a].insert(a);
if(j == 0) { // A -> aXX
first_set[rules[i].LHS].insert(a);
}
}
}
}
do {
changes = false;
for(size_t i = 0; i < rules.size(); i++) {
p = rules[i];
set<string> rfirst = compute_first(p.RHS);
size_t oldsize = first_set[p.LHS].size();
first_set[p.LHS].insert(rfirst.begin(), rfirst.end());
size_t newsize = first_set[p.LHS].size();
if(oldsize != newsize)
changes = true;
}
} while(changes);
}
void Grammar::fill_follow_set() {
string A, B; // nonterminal
Production p;
bool changes;
for(size_t i = 0; i < rules.size(); i++) {
A = rules[i].LHS;
follow_set[A].clear();
}
follow_set[start_symbol].insert(Grammar::lambda);
do {
changes = false;
for(size_t i = 0; i < rules.size(); i++) {
/* A -> a B b
* I.e. for each production and each occurrence
* of a nonterminal in its right-hand side.
*/
p = rules[i];
A = p.LHS;
for(size_t j = 0; j < p.RHS.size(); j++) {
B = p.RHS[j];
if(isNonterminal(B)) {
vector<string> beta(p.RHS.begin() + j + 1, p.RHS.end());
set<string> rfirst = compute_first(beta);
size_t oldsize = follow_set[B].size();
if(rfirst.find(Grammar::lambda) == rfirst.end()) {
follow_set[B].insert(rfirst.begin(), rfirst.end());
} else {
rfirst.erase(Grammar::lambda);
follow_set[B].insert(rfirst.begin(), rfirst.end());
rfirst = follow_set[A];
follow_set[B].insert(rfirst.begin(), rfirst.end());
}
size_t newsize = follow_set[B].size();
if(oldsize != newsize)
changes = true;
}
}
}
} while(changes);
}
void Grammar::lldriver(queue<string> tokens) {
stack<string> stk;
string X;
string a;
Production p;
/* Push the Start Symbol onto an empty stack */
stk.push(start_symbol);
while(!stk.empty() && !tokens.empty()) {
X = stk.top();
a = tokens.front();
// cout << X << " " << a << endl;
if(isNonterminal(X) && lltable[X].find(a) != lltable[X].end()) {
p = lltable[X][a];
stk.pop();
for(int i = p.RHS.size() - 1; i >= 0; i--) {
if(p.RHS[i] == Grammar::lambda)
continue;
stk.push(p.RHS[i]);
}
} else if(X == a) {
stk.pop();
tokens.pop();
} else if(lltable[X].find(Grammar::lambda) != lltable[X].end()) {
stk.pop();
} else {
/* Process syntax error. */
puts("Bad!");
return;
}
}
while(!stk.empty()) {
X = stk.top();
if(lltable[X].find(Grammar::lambda) != lltable[X].end())
stk.pop();
else
break;
}
if(tokens.size() == 0 && stk.size() == 0)
puts("Good");
else
puts("Bad!");
return;
}
queue<string> getTokens(char s[]) {
stringstream sin(s);
queue<string> tokens;
string token;
while(sin >> token)
tokens.push(token);
return tokens;
}
void parsingProduction(string r, Grammar &g) {
#ifdef HTMLProduction
static int production_label = 0;
stringstream sin(r);
string lhs, foo;
vector<string> tokens;
sin >> lhs >> foo;
while(sin >> foo)
tokens.push_back(foo);
Production p(lhs, tokens);
p.label = ++production_label;
g.rules.push_back(p);
#else
string div("->");
size_t found = r.find(div);
if(found != std::string::npos) {
string rhs = r.substr(found + div.length());
vector<string> tokens;
for(size_t i = 0; i < rhs.size(); i++)
tokens.push_back(rhs.substr(i, 1));
Production p(r.substr(0, found), tokens);
g.rules.push_back(p);
}
#endif
}
int main() {
//freopen("input.txt", "r+t", stdin);
//freopen("output.txt", "w+t", stdout);
char in[1024];
while(gets(in)) {
Grammar g;
parsingProduction(in, g);
while(gets(in) && in[0] != '\0') {
parsingProduction(in, g);
}
#ifdef HTMLProduction
g.start_symbol = "<system_goal>";
#else
g.start_symbol = "S";
#endif
g.fill_first_set();
g.fill_follow_set();
g.fill_lltable();
puts("+----------------+----- First set -----+");
for(map<string, set<string> >::iterator it = g.first_set.begin();
it != g.first_set.end(); it++) {
printf("|%-16s| {", it->first.c_str());
for(set<string>::iterator jt = it->second.begin();
jt != it->second.end(); jt++) {
cout << " " << *jt;
}
puts(" }");
}
puts("+----------------+---------------------+\n");
puts("+----------------+----- Follow set -----+");
for(map<string, set<string> >::iterator it = g.follow_set.begin();
it != g.follow_set.end(); it++) {
printf("|%-16s| {", it->first.c_str());
for(set<string>::iterator jt = it->second.begin();
jt != it->second.end(); jt++) {
cout << " " << *jt;
}
puts(" }");
}
puts("+----------------+---------------------+\n");
puts("+----------------+----- LL(1) table -----+");
printf("|%-16s|", "");
for(map<string, set<string> >::iterator jt = g.first_set.begin();
jt != g.first_set.end(); jt++) {
string A = jt->first;
if(g.isNonterminal(A))
continue;
printf("%6s|", A.c_str());
}
puts("");
for(map<string, map<string, Production> >::iterator it = g.lltable.begin();
it != g.lltable.end(); it++) {
printf("|%-16s|", it->first.c_str());
for(map<string, set<string> >::iterator jt = g.first_set.begin();
jt != g.first_set.end(); jt++) {
string A = jt->first;
if(g.isNonterminal(A))
continue;
if(it->second.find(A) == it->second.end())
printf("%6s|", "");
else
printf("%6d|", it->second[A].label);
}
puts("");
}
puts("+----------------+---------------------+\n");
#ifdef HTMLProduction
gets(in);
g.lldriver(getTokens(in));
#endif
}
return 0;
}

備註

要學會更多,請修 計算理論 這門課程。

Read More +

UVa 10378 - Complex Numbers

Problem

Numbers of the form a±bi are called complex numbers (Where i = √(-1)). In this problem you will have to find all the values of (a±bi)1/n.

Input

The input file contains several lines of input. Each line has two parts. The first part will contain a complex number of the form a±bi and the second part will contain an integer n (0 < n <=100). Here a and b are integer numbers and 0 <= |a|, |b|<100. Input is terminated by end of file.

Output

For each line of input you should produce n+2 lines of output. The description of output for each line of input is given in the next paragraph:

First line contains the case number as shown in the sample output. Next n lines should contain the n-th roots of the input complex number. The printed complex numbers should be of the form x±yi, where x and y are floating point numbers rounded upto three digits after the decimal point. Note that there is no space between the operators and operands. The roots are sorted according to descending order of their real part and then according to the descending order of their imaginary part. When the printed value of x or y is 0.000, it should be interpreted as positive zero. So you should never print something like “-0.000”. After all these outputs print a blank line.

Sample Input

1
2
3+4i 2
5-4i 3

Sample Output

1
2
3
4
5
6
7
8
Case 1:
2.000+1.000i
-2.000-1.000i
Case 2:
1.810-0.414i
-0.546+1.775i
-1.264-1.361i

Solution

題目描述:

問虛數 a+bi 的 n 次根號的結果,並且根據實數由大至小、虛數由大至小的字典順序輸出。

題目解法:

先算出 a+bi 所占有的角度 theta,得到 (theta + 2 * pi * i / n) 是每一個根的角度,因為根據角度疊加原理得到 (theta + 2 * pi * i / n) ^ n = theta + 2 * pi * i = theta,但是這題的困難是在於 如何避免 -0.000 的輸出。

原本錯誤排序的寫法

1
2
3
4
5
bool cmp(Point a, Point b) {
if(fabs(a.x - b.x) > eps)
return a.x < b.x;
return a.y < b.y;
}

應更正為如下代碼,如果要問我為什麼,現在還沒有仔細地理解,照理來說應該是差不多的。

1
2
3
4
5
6
7
bool cmp(Point a, Point b) {
if(fabs(a.x - b.x) > eps)
return a.x < b.x;
if(fabs(a.y - b.y) > eps)
return a.y < b.y;
return false;
}

之前是想利用 sprintf(buffer, "%+.3lf%+.3lf", a, b); 去檢查 +0.000-0.000,這種寫法有點太過了,不過應該也是可行的。

lang: cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <algorithm>
using namespace std;
#define eps 1e-6
bool cmp(pair<double, double> a, pair<double, double> b) {
if(fabs(a.first - b.first) > eps)
return a.first > b.first;
if(fabs(a.second - b.second) < eps)
return false;
return a.second > b.second;
}
int main() {
char s[50];
const double pi = acos(-1);
int n, i, cases = 0;
double a, b;
while(scanf("%s %d", s, &n) == 2) {
if(sscanf(s, "%lf+%lfi", &a, &b) == 2)
{}
else
sscanf(s, "%lf-%lfi", &a, &b), b = -b;
double theta = atan2(b, a);
double k = pow(sqrt(a*a+b*b), 1.0/n);
double px, py;
pair<double, double> D[128];
for(i = 0; i < n; i++) {
px = k*cos((theta + i*2*pi)/n);
py = k*sin((theta + i*2*pi)/n);
D[i] = make_pair(px, py);
}
sort(D, D+n, cmp);
printf("Case %d:\n", ++cases);
for(i = 0; i < n; i++) {
if(fabs(D[i].first) < 0.0005) D[i].first = 0;
if(fabs(D[i].second) < 0.0005) D[i].second = 0;
printf("%.3lf%+.3lfi\n", D[i].first, D[i].second);
}
puts("");
}
return 0;
}
Read More +

UVa 1572 - Self-Assembly

Problem

Automatic Chemical Manufacturing is experimenting with a process called self-assembly. In this process, molecules with natural asonity for each other are mixed together in a solution and allowed to spon-taneously assemble themselves into larger structures. But there is one problem: sometimes molecules assemble themselves into a structure of unbounded size, which gums up the machinery.

You must write a program to decide whether a given collection of molecules can be assembled into a structure of unbounded size. You should make two simplifying assumptions: 1) the problem is restricted to two dimensions, and 2) each molecule in the collection is represented as a square. The four edges of the square represent the surfaces on which the molecule can connect to other compatible molecules.

In each test case, you will be given a set of molecule descriptions. Each type of molecule is described by four two-character connector labels that indicate how its edges can connect to the edges of other molecules. There are two types of connector labels:

  • An uppercase letter ( A, …, Z ) followed by + or . Two edges are compatible if their labels have the same letter but different signs. For example, A+ is compatible with A but is not compatible with A+ or B.
  • Two zero digits 00. An edge with this label is not compatible with any edge (not even with
    another edge labeled 00).

Assume there is an unlimited supply of molecules of each type, which may be rotated and reected.
As the molecules assemble themselves into larger structures, the edges of two molecules may be adjacent to each other only if they are compatible. It is permitted for an edge, regardless of its connector label, to be connected to nothing (no adjacent molecule on that edge). Figure A.1 shows an example of three molecule types and a structure of bounded size that can be assembled from them (other bounded structures are also possible with this set of molecules).

Input

The input consists of several test cases. A test case consists of two lines. The rst contains an integer n (1 < n < 40000) indicating the number of molecule types. The second line contains n eight-character strings, each describing a single type of molecule, separated by single spaces. Each string consists of four two-character connector labels representing the four edges of the molecule in clockwise order.

Output

For each test case, display the word unbounded if the set of molecule types can generate a structure of unbounded size. Otherwise, display the word bounded.

Sample Input

1
2
3
4
3
A+00A+A+ 00B+D+A- B-C+00C+
1
K+K-Q+Q-

Sample Output

1
2
bounded
unbounded

Solution

題目描述:

分定數種分子,每一種分子有無限多個,問會不會產生無邊界的化合物,也就是可以一直串一直串下去。

題目解法:

鏡射和旋轉不用去理會,對於建圖並沒有影響,而題目圖片中看起來是平面,但是事實上可以螺旋疊起。

img

如上圖片所示,使用這種建照方式去找環,只要有環的情況就會有無邊界的化合物。

不考慮找到每一個化合物間的關係,這關係過於龐大,只考慮接口連接的關係。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include <stdio.h>
#include <vector>
#include <string.h>
using namespace std;
int g[64][64];
int node(char a, char b) {
return (a - 'A') + (b == '+' ? 0 : 26);
}
int rnode(char a, char b) {
return (a - 'A') + (b == '+' ? 26 : 0);
}
int exist_cycle;
int main() {
int n;
char s[50];
while(scanf("%d", &n) == 1) {
memset(g, 0, sizeof(g));
for(int i = 0; i < n; i++) {
scanf("%s", s);
for(int j = 0; j < 4; j++) {
for(int k = 0; k < 4; k++) {
if(j == k || s[2 * j] == '0' || s[2 * k] == '0')
continue;
int x = node(s[2 * j], s[2 * j + 1]);
int y = rnode(s[2 * k], s[2 * k + 1]);
g[x][y] = 1;
}
}
}
exist_cycle = 0;
n = 52;
for(int k = 0; k < n; k++) {
for(int i = 0; i < n; i++)
for(int j = 0; j < n; j++)
g[i][j] |= g[i][k]&g[k][j];
}
for(int i = 0; i < n; i++)
exist_cycle |= g[i][i];
puts(exist_cycle ? "unbounded" : "bounded");
}
return 0;
}
/*
3
A+00A+A+ 00B+D+A- B-C+00C+
1
K+K-Q+Q-
*/
Read More +

POJ 1166 - The Clocks

Description

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|-------| |-------| |-------|
| | | | | | |
|---O | |---O | | O |
| | | | | |
|-------| |-------| |-------|
A B C
|-------| |-------| |-------|
| | | | | |
| O | | O | | O |
| | | | | | | | |
|-------| |-------| |-------|
D E F
|-------| |-------| |-------|
| | | | | |
| O | | O---| | O |
| | | | | | | |
|-------| |-------| |-------|
G H I

(Figure 1)

There are nine clocks in a 3*3 array (figure 1). The goal is to return all the dials to 12 o’clock with as few moves as possible. There are nine different allowed ways to turn the dials on the clocks. Each such way is called a move. Select for each move a number 1 to 9. That number will turn the dials 90’ (degrees) clockwise on those clocks which are affected according to figure 2 below.

Move Affected clocks
1 ABDE
2 ABC
3 BCEF
4 ADG
5 BDEFH
6 CFI
7 DEGH
8 GHI
9 EFHI

(Figure 2)

Input

Your program is to read from standard input. Nine numbers give the start positions of the dials. 0=12 o’clock, 1=3 o’clock, 2=6 o’clock, 3=9 o’clock.

Output

Your program is to write to standard output. Output a shortest sorted sequence of moves (numbers), which returns all the dials to 12 o’clock. You are convinced that the answer is unique.

Sample Input

1
2
3
3 3 0
2 2 2
2 1 2

Sample Output

1
4 5 8 9

Solution

原來之前寫過這一題,請參閱 d730: 升旗典礼 ——加强版

用 BFS 從 0 0 0 0 0 0 0 0 0 開始倒推回去,在 mark 的部份,用 4 進制 bitmask。

這題在 POJ 似乎只有一組測資,在 Zerojudge 則有非常多筆測資。

而事實上網路上還有另外一個高斯消去法,先預求反矩陣,之後直接帶入計算即可。沒有仔細去研究,不過聽說那解法限定一定有合法解,如何判定不合法解又是另外一個問題。總之,用了 BFS 過了就沒仔細去探討其他解法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
#include <stdio.h>
#include <queue>
using namespace std;
char mark[1<<18] = {}, ans[1<<18], best[1<<18];
char dirp[4] = {-3, 1, 1, 1};
int next[1<<18] = {};
int pow4[10] = {1};
int move[10][7] = { {},
{1, 2, 4, 5, 0},
{1, 2, 3, 0},
{2, 3, 5, 6, 0},
{1, 4, 7, 0},
{2, 4, 5, 6, 8, 0},
{3, 6, 9, 0},
{4, 5, 7, 8, 0},
{7, 8, 9, 0},
{5, 6, 8, 9, 0}
};
void trans(int N, char s[]) {
int i = 0;
while(N) {
s[i++] = N%4, N /= 4;
}
}
void build() {
int a, b, c, qt = 0, t, p;
queue<int> Q;
best[0] = 0, mark[0] = 1;
Q.push(0);
while(!Q.empty()) {
int state = Q.front();
Q.pop();
char buf[10] = {};
trans(state, buf);
for(int i = 1; i < 10; i++) {
int nstate = state;
for(int j = 0; move[i][j]; j++)
nstate -= pow4[move[i][j] - 1] * dirp[buf[move[i][j] - 1]];
if(mark[nstate] == 0) {
mark[nstate] = 1;
next[nstate] = state;
best[nstate] = best[state] + 1;
ans[nstate] = i + '0';
Q.push(nstate);
}
}
}
}
int print_f = 0;
void Print(int state) {
if(state) {
Print(next[state]);
if(print_f)
putchar(' ');
print_f = 1;
putchar(ans[state]);
}
}
int main() {
for(int i = 1; i < 10; i++)
pow4[i] = pow4[i-1] * 4;
build();
while(true) {
int state = 0, x;
for(int i = 0; i < 9; i++) {
if(scanf("%d", &x) != 1)
return 0;
state |= x * pow4[i];
}
print_f = 0;
Print(state);
puts("");
}
return 0;
}
Read More +

UVa 10040 - Ouroboros Snake

Background

Ouroboros was a mythical snake in Ancient Egypt. It has its tail inside its mouth and continuously devours itself.

Problem

Ouroboros numbers are binary numbers of 2n bits that have the property of generating the whole set of numbers from 0 to 2n-1 as follows: To produce all of them we place the 2n bits wrapped in a circle so that the last bit goes before the first one. Then we can denote all 2n different strings with n bits starting each time with the next bit in the circle.

For example, for n=2 there are only four Ouroboros numbers. These are 0011, 0110, 1100 and 1001. For 0011, the following diagram and table depicts the process of finding all the bitstrings:

1
2
3
4
5
k 00110011... o(n=2,k)
0 00 0
1 01 1
2 11 3
3 10 2

Your program will compute the function o(n,k), where n > 0 and $0 \leq k < 2^n$. This function calculates the k-th number inside the smallest Ouroboros number of size n-bits.

Input

The input starts with a line containing the number of test cases. For each test case you will be given a line with two integers n (0 < n <22) and k ( 0 <= k < 2^n).

Output

For every test case your output must evaluate the function o(n,k) and print the result on a line by itself.

Sample Input

1
2
3
4
5
4
2 0
2 1
2 2
2 3

Sample Output

1
2
3
4
0
1
3
2

Solution

題目描述:

有一個 2^n 個 bits 的環,而這個環恰好任取連續 n 個會湊滿 [0, 2^n - 1],現在找一個字典順序最小的環,接著詢問這個環的某一個位置開始的數值。

題目解法:

當詢問 n bits 的結果時,可以把圖建成 2^(n-1) 個節點,每一個節點有兩條邊,邊上分別為 0, 1,也就是說只要繞過所有的邊,相當於產生所有 2^n 個數字。

由於每一個點的 indegree = outdegree 一定存在尤拉迴路,接著在有向圖中找到一條尤拉迴路即可。

至於找字典順序最小環,使用 周源最小表示法 來找,如果可以的話希望能在搜尋時順便找到字典順序最小的。這裡就沒有系不去探究了。

以下是一個最簡單的版本,但這個版本在生成尤拉迴路時會遭遇到 stackoverflow。

TLE version because of stack overflow
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
#include <stdio.h>
#include <string.h>
char g[1<<21][2] = {};
int n, mask, highbit;
char order[2][1<<21], *p;
void dfs(int node) {
int prefix = node;
prefix <<= 1;
for(int i = 0; i < 2; i++) {
if(g[node][i]) {
g[node][i]--;
dfs((prefix|i)&mask);
*p = (i) +'0', p++;
}
}
}
int MinExp(const char *s, const int slen) {
int i = 0, j = 1, k = 0, x, y, tmp;
while(i < slen && j < slen && k < slen) {
x = i + k;
y = j + k;
if(x >= slen) x -= slen;
if(y >= slen) y -= slen;
if(s[x] == s[y]) {
k++;
} else if(s[x] > s[y]) {
i = j+1 > i+k+1 ? j+1 : i+k+1;
k = 0;
tmp = i, i = j, j = tmp;
} else {
j = i+1 > j+k+1 ? i+1 : j+k+1;
k = 0;
}
}
return i;
}
int main() {
int testcase, K;
scanf("%d", &testcase);
while(testcase--) {
scanf("%d %d", &n, &K);
mask = (1<<(n-1)) - 1;
for(int i = (1<<(n-1)) - 1; i >= 0; i--) {
g[i][0]++;
g[i][1]++;
}
p = order[0];
dfs(0);
*p = '\0', p++;
int len = strlen(order[0]);
for(int i = len - 1, j = 0; i >= 0; i--, j++)
order[1][i] = order[0][j];
order[1][len] = '\0';
int o1 = MinExp(order[0], len);
int o2 = MinExp(order[1], len);
int f = 0;
for(int i = 0; i < len; i++) {
if(order[0][(o1 + i)%len] != order[1][(o2 + i)%len]) {
f = order[0][(o1 + i)%len] > order[1][(o2 + i)%len];
break;
}
}
int ret = 0;
for(int i = 0; i < n; i++) {
ret = ret << 1;
if(f == 0) {
ret |= order[0][(o1 + i + K)%len] - '0';
} else {
ret |= order[1][(o2 + i + K)%len] - '0';
}
}
printf("%d\n", ret);
}
return 0;
}

由於上述程式在走訪尤拉迴路時,由於深度過深而造成 stackoverflow 因此需要局部改寫成手動的 stack。通常主機只支持 10 萬到 20 萬之間的深度,這題最慘會到 100 萬。

並且將答案預建出來,如果沒有預建表會導致 Time limit,我以為只有少數幾筆,真是大錯特錯了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
#include <stdio.h>
#include <string.h>
#include <stack>
#include <algorithm>
using namespace std;
char g[1<<21][2] = {};
int n, mask;
char order[2][1<<21], *p;
char ouroboros[22][1<<21];
int olen[22];
void dfs(int node) {
stack< pair<int, int> > stk;
pair<int, int> state;
int line;
stk.push(make_pair(node, 0));
while(!stk.empty()) {
state = stk.top(), stk.pop();
node = state.first, line = state.second&3;
if(line == 0) {
int prefix = node << 1;
if(g[node][0]) {
g[node][0]--;
stk.push(make_pair(node, 1|8));
stk.push(make_pair((prefix|0)&mask, 0));
} else {
stk.push(make_pair(node, 1));
}
} else if(line == 1) {
if(state.second&8) {
*p = (0) +'0', p++;
}
int prefix = node << 1;
if(g[node][1]) {
g[node][1]--;
stk.push(make_pair(node, 2|8));
stk.push(make_pair((prefix|1)&mask, 0));
} else {
stk.push(make_pair(node, 2));
}
} else {
if(state.second&8) {
*p = (1) +'0', p++;
}
}
}
}
int MinExp(const char *s, const int slen) {
int i = 0, j = 1, k = 0, x, y, tmp;
while(i < slen && j < slen && k < slen) {
x = i + k;
y = j + k;
if(x >= slen) x -= slen;
if(y >= slen) y -= slen;
if(s[x] == s[y]) {
k++;
} else if(s[x] > s[y]) {
i = j+1 > i+k+1 ? j+1 : i+k+1;
k = 0;
tmp = i, i = j, j = tmp;
} else {
j = i+1 > j+k+1 ? i+1 : j+k+1;
k = 0;
}
}
return i;
}
void build() {
for(int n = 1; n < 22; n++) {
mask = (1<<(n-1)) - 1;
for(int i = (1<<(n-1)) - 1; i >= 0; i--) {
g[i][0]++;
g[i][1]++;
}
p = order[0];
dfs(0);
*p = '\0', p++;
int len = strlen(order[0]);
for(int i = len - 1, j = 0; i >= 0; i--, j++)
order[1][i] = order[0][j];
order[1][len] = '\0';
int o1 = MinExp(order[0], len);
int o2 = MinExp(order[1], len);
int f = 0;
for(int i = 0; i < len; i++) {
if(order[0][(o1 + i)%len] != order[1][(o2 + i)%len]) {
f = order[0][(o1 + i)%len] > order[1][(o2 + i)%len];
break;
}
}
for(int i = 0; i < len; i++) {
if(f == 0) {
ouroboros[n][i] = order[0][(o1 + i)%len];
} else {
ouroboros[n][i] = order[1][(o2 + i)%len];
}
}
olen[n] = len;
}
}
int main() {
int testcase, K;
build();
scanf("%d", &testcase);
while(testcase--) {
scanf("%d %d", &n, &K);
int ret = 0;
for(int i = 0; i < n; i++) {
ret = ret << 1;
ret |= ouroboros[n][(i + K)%olen[n]] - '0';
}
printf("%d\n", ret);
}
return 0;
}
Read More +

UVa 10089 - Repackaging

儘管如此世界依舊美麗 (Still world is beautiful それでもせかいはうつくしい 插曲) ,比預期中的還要好看,可能是人物屬性的關係讓我欲罷不能啊!順帶附了劇中歌曲,詳細請等待 OST,

Problem

Coffee cups of three different sizes (identified as size 1, size 2 and size 3 cups) are produced in factories under ACM (Association of Cup Makers) and are sold in various packages. Each type of package is identified by three positive integers (S1, S2, S3) where Si (1 <= i <= 3) denotes the number of size i cups included in the package. There is no package with S1 = S2 = S3.

But recently it has been discovered that there is a great demand for packages containing equal number cups of all three sizes. So, as an emergency step to meet the demand ACM has decided to unpack the cups from some of the packages stored in its (unlimited) stock of unsold products and repack them in packages having equal number of cups of all three sizes. For example, suppose ACM has the following four types of packages in its stock: (1, 2, 3), (1, 11, 5), (9, 4, 3) and (2, 3, 2). So, one can unpack three (1, 2, 3) packages, one (9, 4, 3) package and two (2, 3, 2) packages and repack the cups to produce sixteen (1, 1, 1) packages. One can even produce eight (2, 2, 2) packages or four (4, 4, 4) packages or two (8, 8, 8) packages or one (16, 16, 16) package or even different combination of packages each containing equal number of size 1, size 2 and size 3 cups. Note that all the unpacked cups are used to produce the new packages, i.e., no unpacked cup is wasted.

ACM has hired you to write a program that will decide whether it is possible to produce packages containing equal number of all three types of cups using all the cups that can be found by unpacking any combination of existing packages in the stock.

Input

The input may contain multiple test cases. Each test case begins with a line containing an integer N (3 <= N <= 1000) indicating the number of different types of packages that can be found in the stock. Each of the next N lines contains three positive integers denoting respectively the number of size 1, size 2 and size 3 cups in a package. No two packages in a test case will have the same specification.

A test case containing a zero for N in the first line terminates the input.

Output

For each test case in the input print a line containing “Yes” if packages can be produced as desired, print “No” otherwise.

Sample Input

1
2
3
4
5
6
7
8
9
10
11
4
1 2 3
1 11 5
9 4 3
2 3 2
4
1 3 3
1 11 5
9 4 3
2 3 2
0

Sample Output

1
2
Yes
No

Solution

題目描述:

咖啡杯有三種不同大小,出售的時候每一包三者都有不同個數,表示成 (S1, S2, S3) = (小, 中, 大)。

現在要將其重新包裝,每一種包假設有無限個數,問有沒有拆包的方式,滿足

x1 * (a1, b1, c1) + ... + xn * (an, bn, cn) = (k, k, k)

其中 x[i] >= 0 的非負整數, k 為非負整數,也就是重新包裝後要產生 k 個 (1, 1, 1),以方便出售。

題目解法:

整數線性規劃就別來了,畢竟連 k 都沒有確定,解方程也不曉得解集合是否符合條件。

最後,重新考量 (S1, S2, S3),如果以 S1 為基準,則產生 (S2 - S1, S3 - S1) 的向量。(因為最後還是要跟 S1 的總個數去計算,利用相對數量方式去降維。),轉換原本的滿足方程

x1 * (y1, z1) + ... + xn * (yn, zn) = (0, 0)

  • 如果兩個二維向量 (a, b)(c, d),對於任意 p (a, b) + q (c, d) 的結果可以產生兩個向量間任意角度(<= 180 deg)的向量 (不考慮向量長度,p, q 是非負整數)。

明白了上述條件後,將所有包分配至以原點拉出的向量,只要能產生兩個反向向量就可以相消找到 (0, 0) 向量。相消的條件很簡單,使用極角排序,相鄰兩個角度差全小於 pi 即有存在相消情況。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <stdio.h>
#include <vector>
#include <algorithm>
#include <math.h>
using namespace std;
int main() {
int n, S1, S2, S3;
const double pi = acos(-1);
while(scanf("%d", &n) == 1 && n) {
double A[1024];
for(int i = 0; i < n; i++) {
scanf("%d %d %d", &S1, &S2, &S3);
A[i] = atan2(S2 - S1, S3 - S1);
}
sort(A, A + n);
double gap = 0;
for(int i = 1; i < n; i++) {
gap = max(gap, A[i] - A[i-1]);
}
gap = max(gap, 2 * pi - (A[n-1] - A[0]));
puts(gap > pi ? "No" : "Yes");
}
return 0;
}
Read More +

UVa 12544 - Beehives

Problem

Bees are one of the most industrious insects. Since they collect nectarand pollen from owers, they have to rely on the trees in the forest. For simplicity they numbered the n trees from 0 to n - 1. Instead of roaming around all over the forest, they use a particular list of paths. A path is based on two trees, and they can move either way i.e. from one tree to another in straight line. They don’t use paths thatare not in their list.

As technology has been improved a lot, they also changed their working strategy. Instead of hovering over all the trees in the forest, they are targeting particular trees, mainly trees with lots of owers. So, they planned that they will build some new hives in some targeted trees. After that they will only collect their foods from these trees. They will also remove some paths from their list so that they don’t have to go to a tree with no hive in it.

Now, they want to build the hives such that if one of the paths in their new list go down (some
birds or animals disturbs them in that path) it’s still possible to go from any hive to another using the existing paths.

They don’t want to choose less than two trees and as hive-building requires a lot of work, they need to keep the number of hives as low as possible. Now you are given the trees with the paths they use, your task is to propose a new bee hive colony for them.

Input

Input starts with an integer T( T < 50), denoting the number of test cases.

Each case starts with a blank line. Next line contains two integers n (2 <= n <= 500) and m (0 < m< 20000), where n denotes the number of trees and m denotes the number of paths. Each of the next m lines contains two integers uv (0 < u , v < n), u = v) meaning that there is a path between tree u and v. Assume that there can be at most one path between tree u to v, and needless to say that a path will not be given more than once in the input.

Output

For each case, print the case number and the number of beehives in the proposed colony or impossible if its impossible to find such a colony.
NOTE:
Dataset is huge. Use faster I/O methods.

Sample Input

1
2
3
4
5
6
7
8
9
10
11
12
13
14
3
3 3
0 1
1 2
2 0
2 1
0 1
5 6
0 1
1 2
1 3
2 3
0 4
3 4

Sample Output

1
2
3
Case 1: 3
Case 2: impossible
Case 3: 3

Solution

題目描述:

在一個無向圖中,找到一個成本最小環。

題目解法:

Floyd 解法:
先來附一個錯誤版本,可以在 O(n^3) 解決,核心思路在於討論路徑上經過編號小於等於 k 時,同時探討最小環上經過編號 k 個所有環。但由於 n = 500 在此題中效率不好,但也不知道為什麼拿了 Runtime Error

TLE version
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
int g[512][512], w[512][512];
int main() {
int testcase, cases = 0;
int n, m, x, y;
scanf("%d", &testcase);
while(testcase--) {
scanf("%d %d", &n, &m);
for(int i = 0; i < n; i++)
for(int j = 0; j < n; j++)
g[i][j] = w[i][j] = 0xf3f3f3f;
while(m--) {
scanf("%d %d", &x, &y);
w[x][y] = w[y][x] = g[x][y] = g[y][x] = 1;
}
int ret = 0xf3f3f3f;
for(int k = 0; k < n; k++) {
for(int i = 0; i < n; i++) {
for(int j = 0; j < n; j++) {
if(i != j)
ret = min(ret, w[k][i] + g[i][j] + w[j][k]);
}
}
for(int i = 0; i < n; i++)
for(int j = 0; j < n; j++)
g[i][j] = min(g[i][j], g[i][k] + g[k][j]);
}
printf("Case %d: ", ++cases);
if(ret == 0xf3f3f3f)
puts("impossible");
else
printf("%d\n", ret);
}
return 0;
}

BFS 作法:
Floyd 用在成本不同的路徑上是有效的,但是在成本都是 1 的時候就有點太過了,使用 BFS 對於每一個點進行最短路徑,查看路徑樹上是否有 back edge。整體可以在 O(n^2) 完成,這個 back edge 可能會造成有重複走過點的環,但是不影響我們去找到最小成本。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include <stdio.h>
#include <string.h>
#include <vector>
#include <queue>
#include <algorithm>
using namespace std;
vector<int> g[512];
int dist[512], parent[512];
int bfs(int st, int &ret) {
memset(dist, 0, sizeof(dist));
dist[st] = 1, parent[st] = -1;
queue<int> Q;
Q.push(st);
while(!Q.empty()) {
int u = Q.front();
Q.pop();
for(int i = 0; i < g[u].size(); i++) {
int v = g[u][i];
if(v == parent[u]) continue;
if(dist[v] == 0) {
dist[v] = dist[u] + 1;
parent[v] = u;
Q.push(v);
} else {
ret = min(ret, dist[u] + dist[v] - 1);
}
}
}
}
int main() {
int testcase, cases = 0;
int n, m, x, y;
scanf("%d", &testcase);
while(testcase--) {
scanf("%d %d", &n, &m);
for(int i = 0; i < n; i++)
g[i].clear();
while(m--) {
scanf("%d %d", &x, &y);
g[x].push_back(y);
g[y].push_back(x);
}
int ret = n + 1;
for(int i = 0; i < n; i++)
bfs(i, ret);
printf("Case %d: ", ++cases);
if(ret == n + 1)
puts("impossible");
else
printf("%d\n", ret);
}
return 0;
}
Read More +