原題:
城市規劃
時間限制(普通/Java):1000MS/3000MS 運行內存限制:65536KByte
總提交:75 測試通過:13
描述
NanJing準備開發一片荒地,目前已經規劃好了一些居民點,還要建設道路。由于經費問題,現在想在保持任意兩點間的距離最短的前提下,用盡可能少的經費把這些點連接起來。需要注意的是并不是任意兩個居民點間都能直接相連?,F在給出兩兩居民點間的花費,當然了,花費和路徑長度成正比~
輸入
第一行是個N<=100,表示N個居民點。
下面是個N*N的矩陣,第i行第j列,表示i到j的花費,可能有負數,表示兩地不相連。保證有解。
輸出
輸出一行為總花費。
樣例輸入
3
0 2 1
2 0 3
1 3 0
樣例輸出
3
提示
這里建設1到2 和1到3這兩條路。
剛拿到這道題還以為是floyd,仔細看看才發現發現原來不是。
題目中說要保證每個頂點之間的距離最短,也就是說在某個源點到其他點的最短路徑上的通路必須保留,其余的邊可以濾去;
我的第一個想法是不斷的調用DIJ把每個點到其他點的最短路求出來,不過這樣有的邊會被重復加。
后來又有了第二個想法,就是用一個二維矩陣做為標志,如果這條邊(u,v)已經被訪問過,那么map[u][v]置成1,這樣便解決了重復添加的問題。
這樣應該對了吧?我當時也是這樣認為的,可惜結果不然。
如果兩點間有兩條最短路同時存在的情況,該怎么辦呢?由于DIJ每一次循環都會找到一條最短路徑,那么當用這個確定點去更新其他點的信息時,要使用dis[mark]+map[mark][i]<=di[i]而不是<,這樣當出現兩條或者兩條以上的最短路路時會優先選擇已經選擇過的點,這樣便解決了優先權的問題。
好了,經歷的這三個步驟,代碼終于AC.呵呵 (Wa了四次)
#include<iostream>
#include<algorithm>
using namespace std;
#define MAX 101
#define MAX_INT 999999999


int mymap[MAX][MAX];
int visit[MAX];
int dis[MAX];
int pre[MAX];
int record[MAX][MAX];
int n;




int Dij_plus(int s)


{
int result=0;
memset(visit,0,sizeof(visit));
memset(pre,0,sizeof(pre));
int i,j;
for(i=1;i<=n;i++)

{
dis[i]=mymap[s][i];
}
visit[s]=1;
int temp=MAX_INT;
int mark;
for(i=1;i<=n;i++)
pre[i]=-1;
for(i=1;i<=n;i++)

{
if(visit[i]!=1&&mymap[s][i]!=MAX_INT)
pre[i]=s;
}
for(j=1;j<=n-1;j++)

{
temp=MAX_INT;
for(i=1;i<=n;i++)

{
if(visit[i]!=1&&dis[i]<temp)

{
temp=dis[i];
mark=i;
}
}
visit[mark]=1;
if(record[pre[mark]][mark]==0)

{
record[pre[mark]][mark]=1;
record[mark][pre[mark]]=1;
result+=mymap[pre[mark]][mark];
}

for(i=1;i<=n;i++)

{
if(visit[i]!=1&&mymap[mark][i]+dis[mark]<=dis[i])

{
dis[i]=mymap[mark][i]+dis[mark];
pre[i]=mark;
}
}
}
return result;
}

int main ()


{

int i,j;
int result=0;
scanf("%d",&n);
for(i=1;i<=n;i++)

{
for(j=1;j<=n;j++)

{

int temp;
scanf("%d",&temp);
if(temp<0)

{
mymap[i][j]=MAX_INT;
mymap[j][i]=MAX_INT;
continue;
}
mymap[i][j]=temp;
mymap[j][i]=temp;

}
}
for(i=1;i<=n;i++)

{

result+=Dij_plus(i);
}
printf("%d\n",result);
system("pause");
return 0;
}

它是第一個既能用于數據加密也能用于數字簽名的算法。它易于理解和操作,也很流行。算法的名字以發明者的名字命名:Ron Rivest, Adi Shamir 和Leonard Adleman。但RSA的安全性一直未能得到理論上的證明。它經歷了各種攻擊,至今未被完全攻破。
一、RSA算法 :
首先, 找出三個數, p, q, r,
其中 p, q 是兩個相異的質數, r 是與 (p-1)(q-1) 互質的數......
p, q, r 這三個數便是 private key
接著, 找出 m, 使得 rm == 1 mod (p-1)(q-1).....
這個 m 一定存在, 因為 r 與 (p-1)(q-1) 互質, 用輾轉相除法就可以得到了.....
再來, 計算 n = pq.......
m, n 這兩個數便是 public key
編碼過程是, 若資料為 a, 將其看成是一個大整數, 假設 a < n....
如果 a >= n 的話, 就將 a 表成 s 進位 (s <= n, 通常取 s = 2^t),
則每一位數均小於 n, 然後分段編碼......
接下來, 計算 b == a^m mod n, (0 <= b < n),
b 就是編碼後的資料......
解碼的過程是, 計算 c == b^r mod pq (0 <= c < pq),
於是乎, 解碼完畢...... 等會會證明 c 和 a 其實是相等的
如果第三者進行竊聽時, 他會得到幾個數: m, n(=pq), b......
他如果要解碼的話, 必須想辦法得到 r......
所以, 他必須先對 n 作質因數分解.........
要防止他分解, 最有效的方法是找兩個非常的大質數 p, q,
使第三者作因數分解時發生困難.........
<定理>
若 p, q 是相異質數, rm == 1 mod (p-1)(q-1),
a 是任意一個正整數, b == a^m mod pq, c == b^r mod pq,
則 c == a mod pq
證明的過程, 會用到費馬小定理, 敘述如下:
m 是任一質數, n 是任一整數, 則 n^m == n mod m
(換另一句話說, 如果 n 和 m 互質, 則 n^(m-1) == 1 mod m)
運用一些基本的群論的知識, 就可以很容易地證出費馬小定理的........
<證明>
因為 rm == 1 mod (p-1)(q-1), 所以 rm = k(p-1)(q-1) + 1, 其中 k 是整數
因為在 modulo 中是 preserve 乘法的
(x == y mod z and u == v mod z => xu == yv mod z),
所以, c == b^r == (a^m)^r == a^(rm) == a^(k(p-1)(q-1)+1) mod pq
1. 如果 a 不是 p 的倍數, 也不是 q 的倍數時,
則 a^(p-1) == 1 mod p (費馬小定理) => a^(k(p-1)(q-1)) == 1 mod p
a^(q-1) == 1 mod q (費馬小定理) => a^(k(p-1)(q-1)) == 1 mod q
所以 p, q 均能整除 a^(k(p-1)(q-1)) - 1 => pq | a^(k(p-1)(q-1)) - 1
即 a^(k(p-1)(q-1)) == 1 mod pq
=> c == a^(k(p-1)(q-1)+1) == a mod pq
2. 如果 a 是 p 的倍數, 但不是 q 的倍數時,
則 a^(q-1) == 1 mod q (費馬小定理)
=> a^(k(p-1)(q-1)) == 1 mod q
=> c == a^(k(p-1)(q-1)+1) == a mod q
=> q | c - a
因 p | a
=> c == a^(k(p-1)(q-1)+1) == 0 mod p
=> p | c - a
所以, pq | c - a => c == a mod pq
3. 如果 a 是 q 的倍數, 但不是 p 的倍數時, 證明同上
4. 如果 a 同時是 p 和 q 的倍數時,
則 pq | a
=> c == a^(k(p-1)(q-1)+1) == 0 mod pq
=> pq | c - a
=> c == a mod pq
Q.E.D.
這個定理說明 a 經過編碼為 b 再經過解碼為 c 時, a == c mod n (n = pq)....
但我們在做編碼解碼時, 限制 0 <= a < n, 0 <= c < n,
所以這就是說 a 等於 c, 所以這個過程確實能做到編碼解碼的功能.....
二、RSA 的安全性
RSA的安全性依賴于大數分解,但是否等同于大數分解一直未能得到理論上的證明,因為沒有證明破解 RSA就一定需要作大數分解。假設存在一種無須分解大數的算法,那它肯定可以修改成為大數分解算法。目前, RSA 的一些變種算法已被證明等價于大數分解。不管怎樣,分解n是最顯然的攻擊方法。現在,人們已能分解多個十進制位的大素數。因此,模數n 必須選大一些,因具體適用情況而定。
三、RSA的速度
由于進行的都是大數計算,使得RSA最快的情況也比DES慢上倍,無論是軟件還是硬件實現。速度一直是RSA的缺陷。一般來說只用于少量數據加密。
四、RSA的選擇密文攻擊
RSA在選擇密文攻擊面前很脆弱。一般攻擊者是將某一信息作一下偽裝( Blind),讓擁有私鑰的實體簽署。然后,經過計算就可得到它所想要的信息。實際上,攻擊利用的都是同一個弱點,即存在這樣一個事實:乘冪保留了輸入的乘法結構:
( XM )^d = X^d *M^d mod n
前面已經提到,這個固有的問題來自于公鑰密碼系統的最有用的特征--每個人都能使用公鑰。但從算法上無法解決這一問題,主要措施有兩條:一條是采用好的公鑰協議,保證工作過程中實體不對其他實體任意產生的信息解密,不對自己一無所知的信息簽名;另一條是決不對陌生人送來的隨機文檔簽名,簽名時首先使用One-Way HashFunction 對文檔作HASH處理,或同時使用不同的簽名算法。在中提到了幾種不同類型的攻擊方法。
五、RSA的公共模數攻擊
若系統中共有一個模數,只是不同的人擁有不同的e和d,系統將是危險的。最普遍的情況是同一信息用不同的公鑰加密,這些公鑰共模而且互質,那末該信息無需私鑰就可得到恢復。設P為信息明文,兩個加密密鑰為e1和e2,公共模數是n,則:
C1 = P^e1 mod n
C2 = P^e2 mod n
密碼分析者知道n、e1、e2、C1和C2,就能得到P。
因為e1和e2互質,故用Euclidean算法能找到r和s,滿足:
r * e1 + s * e2 = 1
假設r為負數,需再用Euclidean算法計算C1^(-1),則
( C1^(-1) )^(-r) * C2^s = P mod n
另外,還有其它幾種利用公共模數攻擊的方法。總之,如果知道給定模數的一對e和d,一是有利于攻擊者分解模數,一是有利于攻擊者計算出其它成對的e’和d’,而無需分解模數。解決辦法只有一個,那就是不要共享模數n。
RSA的小指數攻擊。 有一種提高 RSA速度的建議是使公鑰e取較小的值,這樣會使加密變得易于實現,速度有
所提高。但這樣作是不安全的,對付辦法就是e和d都取較大的值。
RSA算法是第一個能同時用于加密和數字簽名的算法,也易于理解和操作。RSA是被研究得最廣泛的公鑰算法,從提出到現在已近二十年,經歷了各種攻擊的考驗,逐漸為人們接受,普遍認為是目前最優秀的公鑰方案之一。RSA的安全性依賴于大數的因子分解,但并沒有從理論上證明破譯RSA的難度與大數分解難度等價。即RSA的重大缺陷是無法從理論上把握它的保密性能如何,而且密碼學界多數人士傾向于因子分解不是NPC問題。 RSA的缺點主要有:A)產生密鑰很麻煩,受到素數產生技術的限制,因而難以做到一次一密。B)分組長度太大,為保證安全性,n 至少也要 600 bits 以上,使運算代價很高,尤其是速度較慢,較對稱密碼算法慢幾個數量級;且隨著大數分解技術的發展,這個長度還在增加,不利于數據格式的標準化。目前,SET( Secure Electronic Transaction )協議中要求CA采用比特長的密鑰,其他實體使用比特的密鑰。
前一陣子看了電視劇《暗算》,蠻喜歡它的構思和里面的表演。其中有一個故事提到了密碼學,故事本身不錯,但是有點故弄玄虛。不過有一點是對的,就是當今的密碼學是以數學為基礎的。(沒有看過暗算的讀者可以看一下介紹,
http://ent.sina.com.cn/v/2005-10-17/ba866985.shtml因為我們后面要多次提到這部電視劇。)
密碼學的歷史大致可以推早到兩千年前,相傳名將凱撒為了防止敵方截獲情報,用密碼傳送情報。凱撒的做法很簡單,就是對二十幾個羅馬字母建立一張對應表,比如說

這樣,如果不知道密碼本,即使截獲一段信息也看不懂,比如收到一個的消息是 EBKTBP,那么在敵人看來是毫無意義的字,通過密碼本解破出來就是 CAESAR 一詞,即凱撒的名字。這種編碼方法史稱凱撒大帝。當然,學過信息論的人都知道,只要多截獲一些情報,統計一下字母的頻率,就可以解破出這種密碼??滤{道爾在他的“福爾摩斯探案集”中“跳舞的小人”的故事里已經介紹了這種小技巧。在很長時間里,人們試圖找到一些好的編碼方法使得解密者無法從密碼中統計出明碼的統計信息,但是,基本上靠經驗。有經驗的編碼者會把常用的詞對應成多個密碼, 使得破譯者很難統計出任何規律。比如,如果將漢語中的“是”一詞對應于唯一一個編碼 0543,那么破譯者就會發現 0543 出現的特別多。但如果將它對應成十個密碼 0543,3737,2947 等等,每次隨機的挑一個使用,每個密碼出現的次數就不會太多,而且破譯者也無從知道這些密碼其實對應一個字。這里面雖然包含著樸素的概率論的原理,但是并不科學化。另外,好的密碼必須做到不能根據已知的明文和密文的對應推斷出新的密文的內容。歷史上有很多在這方面設計得不周到的密碼的例子。在第二次世界大戰中,日本軍方的密碼設計就很成問題。美軍破獲了日本很多密碼。在中途島海戰前,美軍截獲的日軍密電經常出現 AF 這樣一個地名,應該是太平洋的某個島嶼,但是美軍無從知道是哪個。于是,美軍就逐個發表自己控制的每個島嶼上的假新聞。當美軍發出“中途島供水系統壞了”這條假新聞后,從截獲的日軍情報中又看到 AF 供水出來問題的電文,美軍就斷定中途島就是 AF。事實證明判斷正確,美軍在那里成功地伏擊了日本主力艦隊。
事實上,在第二次世界大戰中,很多頂尖的科學家包括提出信息論的香農都在為美軍情報.部門工作,而信息論實際上就是情報學的直接產物。香農提出信息論后,為密碼學的發展帶來了新氣象。根據信息論,密碼的最高境界是使得敵人在截獲密碼后,對我方的所知沒有任何增加,用信息論的專業術語講,就是信息量沒有增加。一般來講,當密碼之間分布均勻并且統計獨立時,提供的信息最少。均勻分布使得敵人無從統計,而統計獨立能保證敵人即使看到一段密碼和明碼后,不能破譯另一段密碼。這也是《暗算》里傳統的破譯員老陳破譯的一份密報后,但無法推廣的原因,而數學家黃依依預見到了這個結果,因為她知道敵人新的密碼系統編出的密文是統計獨立的。有了信息論后,密碼的設計就有了理論基礎,現在通用的公開密鑰的方法,包括《暗算》里的“光復一號”密碼,就是基于這個理論。
公開密鑰的原理其實很簡單,我們以給上面的單詞 Caesar 加解密來說明它的原理。我們先把它變成一組數,比如它的 Ascii 代碼 X=099097101115097114(每三位代表一個字母)做明碼。現在我們來設計一個密碼系統,對這個明碼加密。
1,找兩個很大的素數(質數)P 和 Q,越大越好,比如 100 位長的, 然后計算它們的乘積 N=P×Q,M=(P-1)×(Q-1)。
2,找一個和 M 互素的整數 E,也就是說 M 和 E 除了 1 以外沒有公約數。
3,找一個整數 D,使得 E×D 除以 M 余 1,即 E×D mod M = 1。
現在,世界上先進的、最常用的密碼系統就設計好了,其中 E 是公鑰誰都可以用來加密,D 是私鑰用于解密,一定要自己保存好。乘積 N 是公開的,即使敵人知道了也沒關系。
現在,我們用下面的公式對 X 加密,得到密碼 Y。

好了,現在沒有密鑰 D,神仙也無法從 Y 中恢復 X。如果知道 D,根據費爾馬小定理,則只要按下面的公式就可以輕而易舉地從 Y 中得到 X。

這個過程大致可以概況如下:

公開密鑰的好處有:
1.簡單。
2.可靠。公開密鑰方法保證產生的密文是統計獨立而分布均勻的。也就是說,不論給出多少份明文和對應的密文,也無法根據已知的明文和密文的對應來破譯下一份密文。更重要的是 N,E 可以公開給任何人加密用,但是只有掌握密鑰 D 的人才可以解密, 即使加密者自己也是無法解密的。這樣,即使加密者被抓住叛變了,整套密碼系統仍然是安全的。(而凱撒大帝的加密方法有一個知道密碼本的人泄密,整個密碼系統就公開了。)
3.靈活,可以產生很多的公開密鑰E和私鑰D的組合給不同的加密者。
最后讓我們看看破解這種密碼的難度。首先,要聲明,世界上沒有永遠破不了的密碼,關鍵是它能有多長時間的有效期。要破公開密鑰的加密方式,至今的研究結果表明最好的辦法還是對大字 N 進行因數分解,即通過 N 反過來找到 P 和 Q,這樣密碼就被破了。而找 P 和 Q 目前只有用計算機把所有的數字試一遍這種笨辦法。這實際上是在拼計算機的速度,這也就是為什么 P 和 Q 都需要非常大。一種加密方法只有保證 50 年計算機破不了也就可以滿意了。前幾年破解的 RSA-158 密碼是這樣因數分解的
395058745832651445264197678006144819960207764603049364541393760515793556265294
50683609727842468219535093544305870490251995655335710209799226484977949442955603
= 3388495837466721394368393204672181522815830368604993048084925840555281177 ×11658823406671259903148376558383270818131012258146392600439520994131344334162924536139
現在,讓我們回到《暗算》中,黃依依第一次找的結果經過一系列計算發現無法歸零,也就是說除不盡,我猜她可能試圖將一個大數 N 做分解,沒成功。第二次計算的結果是歸零了,說明她找到的 N=P×Q 的分解方法。當然,這件事能不能用算盤完成,我就不知道了,但我覺得比較夸張。另外我對該電視劇還有一個搞不懂的問題就是里面提到的“光復一號”密碼的誤差問題。一個密碼是不能有誤差的,否則就是有的密鑰也無法解碼了。我想可能是指在構造密碼時,P 和 Q 之一沒找對,其中一個(甚至兩個都)不小心找成了合數,這時密碼的保密性就差了很多。如果誰知道電視劇里面講的“誤差”是指什么請告訴我。另外,電視劇里提到馮?諾依曼,說他是現代密碼學的祖宗,我想是弄錯了,應該是香農。馮?諾依曼的貢獻在發明計算機和提出博弈論(game theory)。
不管怎么樣,我們今天用的所謂最可靠的加密方法的數學原理其實就這么簡單,一點也不神秘,無非是找幾個大素數做一些乘除和乘方運算就可以了。