Time of Update: 2018-12-07
UVA_270 這個題目我只想到了最佳化常數的辦法,沒想到AC了,但耗時還是不容樂觀的,這個題好像是有n^2logn的演算法,要先進行排序再處理,但暫時還沒有找到相應的詳細一點的思路。 最佳化常數的辦法就是用一個鄰接矩陣記錄一下哪兩個點以前已經在某條直線上被算過了,那麼在枚舉的時候就不用再枚舉這兩個點作為一條直線了。
Time of Update: 2018-12-07
UVA_10148 這個問題和區間選點問題是類似的。我們可以首先把區間按右端點排序,然後從左向右依次安排廣告牌,對於每個區間,我們應該盡量將廣告牌安排在右邊,這樣能夠使廣告牌覆蓋儘可能多的區間。#include<stdio.h>#include<string.h>#include<stdlib.h>#define MAXD 20010#define MAXN 1010#define D 10000int a[MAXN], b[MAXN], K, N,
Time of Update: 2018-12-07
SGU_116 一開始用回溯寫了一下,發現效率還是很高的,但需要注意不是當前選擇的越大就一定越好。 後來看了別人的題解提到了dp的解法,於是便又用dp寫了一下,用f[i]表示i最少可以分解為幾個超級素數之和,同時用fa[]記錄下決策的過程即可。//回溯#include<stdio.h>#include<string.h>#define MAXD 10010#define INF 0x3f3f3f3fint N, isprime[MAXD],
Time of Update: 2018-12-07
UVA_10029 這個題目一開始想轉化成最長上升子序列的模型,但發現這樣不好做,後來看了別人的題解發現可以轉化成找有向非循環圖的最長路去做。 這樣面臨的一個問題就是如果顯性地建圖的話是沒辦法存下每條邊的,於是我們不妨在每次嘗試連線的時候再判斷這條邊是否存在。 為了方便判斷兩個字串之間是否有一條有向邊,我們不妨以起點為基礎,施加3種變換,觀察是否可以變換出字典裡的字串,如果可以變換出的話,就說明這兩個字串之間存在一條有向邊。
Time of Update: 2018-12-07
UVA_11008 這個題目由於樹的數目很少,我們可以把樹的狀態壓縮成一個整數,並依此來進行狀態轉移。 為了方面後續的狀態轉移,我們可以預先處理出任意兩個樹連線上樹的分布情況,處理的時候同樣選擇把狀態壓縮成一個整數,這樣在後面就只需要一個位元運算的式子就可以消去這條線上所有的樹了。 這樣對於一個樹林的狀態tree,我們就可以枚舉其中兩棵樹,打掉這條線上的所有樹就可以了,用記憶化搜尋實現起來會比較方便。
Time of Update: 2018-12-07
UVA_10604 一開始看錯題了,以為化學物質最多會有10個,所以定義不了10維的去跑,便用了類似狀態壓縮的方式,把化學物質的狀態壓縮成一個整數,然後用雜湊表建立一個索引,再用記憶化搜素去處理就可以了。 之所以能這麼做,關鍵在於總狀態並不是很多,我們可以粗略的估算一下,即便有10種藥品時,狀態最多有(10^10)/10!,考慮到混合之後達到的狀態,最多再乘個10而已,用計算機算一下,數值還是比較小的。
Time of Update: 2018-12-07
UVA_10635 這個題目顯然用n^2的最長公用子序列的演算法會逾時,後來看了別人的解題報告發現,原來最長公用子序列可以轉化成最長上升子序列的問題,然後用最長上升子序列的nlogn的演算法求解。
Time of Update: 2018-12-07
UVA_138 這個題目讓我第一次過了一把打表的癮,自從悠然告訴我真正的打表法該怎麼用了以後,這是第一次實踐。 當然後來又學了一種佩爾方程解法,根據已知我們可以化簡得到一個二元一次方程(2n+1)^2-8*m^2=1,然後找到基本解2n+1=3和m=1,依據遞推公式找到後面的10對m、n列印出來即可。//打表程式#include<stdio.h>#include<string.h>int main(){ freopen("UVA_138(1).cpp",
Time of Update: 2018-12-07
UVA_10723 首先這個題目肯定是按最長公用子序列的形式進行dp的,因為只有保證消去的一部分是最長公用子序列才能保證最後產生的序列最短。 因此,在記錄方案數的時候我們也按最長公用子序列的產生過程來記錄即可,我們不妨用p[i][j]記錄最長公用子序列的字元數,用f[i][j]表示到第一個字串第i個位置、第二個字串第j個位置時產生的序列最短的方案種數。 當a[i]!=b[j]時,p[i][j]=max{p[i-1][j],p[i][j-1]},那麼如果p[i][j]==p[i-
Time of Update: 2018-12-07
UVA_11151 狀態轉移方程為:①b[i]==b[j],f[i][j]=f[i+1][j-1]+(i==j?1:2);②b[i]!=b[j],f[i][j]=max{f[i+1][j],f[i][j-1]}。#include<stdio.h>#include<string.h>#define MAXD 1010int N, f[MAXD][MAXD];char b[MAXD];void solve(){int i, j, k; gets(b); N
Time of Update: 2018-12-07
UVA_10280 沒想到這個題目用一個剪枝和感覺上複雜度比較大的dp就可以過掉,當然這個題目的複雜度如果真仔細算起來感覺還是比較麻煩的,我在後面也提了幾點設想,也許真正的複雜度沒有到我們想象得那麼大。 先把剪枝放在這裡,設limit=min{max*min/(max-min)},那麼如果酒量是大於limit的,就必然能夠全部裝下,否則我們再執行dp。 下面我們開始證明這個剪枝的正確性。我們不妨先對一類瓶子進行討論,設其最小、最大的裝酒量分別是min、max,實際上如果有[x/
Time of Update: 2018-12-07
UVA_10183 根據通項公式粗略計算一下,我們可以知道10^100內的斐波那契數不會超過600個,因此只需要預先處理出這些斐波那契數並掃描一遍即可知道在指定範圍內的個數。import java.math.BigInteger;import java.util.Scanner;public class Main {public static void main(String[] args) { Scanner cin = new Scanner(System.in);
Time of Update: 2018-12-07
SGU_104 我們可以用f[i][j]、p[i][j]分別表示第i行掃描第j個瓶子時的最大值以及取得這個最大值時第i行的花放到了哪個瓶子中。#include<stdio.h>#include<string.h>#define MAXD 110#define INF 0x3fffffffint F, V, f[MAXD][MAXD], v[MAXD][MAXD], p[MAXD][MAXD];void printpath(int i, int j){if(i !=
Time of Update: 2018-12-07
UVA_11027 首先如果要組成迴文串,最多隻能有一種字母是奇數個。 然後我們不妨設要找第N個迴文串,那麼自然先挑字典序最小的一對放在兩端,這樣算一下這種情況一共有p種,如果p<N,說明當前應該放字典序更大一點的,於是我們令N=N-p,然後再嘗試下一種方法。
Time of Update: 2018-12-07
UVA_10862 我們不妨設f(n)為n個方塊時情況的種數,那麼我們不妨從f(n-1)入手,在後面添加第n個方塊,看看一共有多少種添加進去的方式。 為了討論的方便,我們對第n個方塊標號為1,第n-1個標號為2,等等,然後畫出下面這個圖。 (圓圈) 方塊n …… 方塊3 方塊2 方塊1 首先,方塊1是可以在f(n-1)的基礎之上連上圓圈,這樣就有f(n-1)情況了,其次方塊1可以在f(n-1)的基礎之上連上方塊2
Time of Update: 2018-12-07
UVA_10128 這個題目一開始用排列組合去想的,比較麻煩,後來看了別人的解題報告,發現用遞推去想比較簡單。 先說說排列組合的思路吧,最高的人肯定是擋不住的,然後最高的人把隊列分成了兩部分,兩部分分別從某個方向看過去人數分別為P-1和R-1(因為這個時候我們不考慮最高的人)。由於兩部分是對稱的,因此我們不妨f[i][j]表示i個人時,從一個方向看過去能夠看到j個人的情況種數,這樣我們只要把f[i][j]都求出來了,最後再用組合數分一下最高的人兩邊的人數即可。 在計算f[i][
Time of Update: 2018-12-07
UVA_10236 這個題目AC得太辛苦了,也再一次加深了對浮點數誤差的理解,庫函數能不調還是最好別調……其實後來感覺就像UVA論壇上一個人說的,AC這個題有兩個準則,第一個是要用long double,第二個是不要相信fmod、sqrt、pow等這些庫函數,它們都會帶來精度上的問題。
Time of Update: 2018-12-07
POJ_2752 將字串對自己進行KMP匹配即可,也即進行一般的KMP問題的預先處理的步驟。之後去找恰好匹配到最後一個字元的所有可能的首碼的長度即可,也就是遍曆一遍P[N],其中N為字串的長度。#include<stdio.h>#include<string.h>#define MAXD 400010char b[MAXD];int P[MAXD];void printresult(int k){if(P[k] == 0) printf("%d",
Time of Update: 2018-12-07
HNU_10076 這個和POJ的3332十分類似,都是判斷一個字串是否合法,具體怎麼想可以參考一下我的另一篇題解,http://www.cnblogs.com/staginner/archive/2012/01/24/2329131.html,代碼為了看起來思路清晰一些,所以寫得就十分囉嗦了,很多地方是可以簡化的。#include<stdio.h>#include<string.h>#define MAXD 10010char noun[][10] =
Time of Update: 2018-12-07
POJ_1743 搜到的題解基本都是說去找最長的不重疊的重複的子串,但是我一直對題目描述裡面的那個const帶有一點糾結,比如88 88 88 1 1 1 88 88 88 1 1 1,我覺得要按const去理解的話應該輸出3的,但題解都是輸出的6。不過意在練尾碼數組的使用,就沒有太顧及這些問題了。 這個題目關鍵的地方有兩個:第一,重複的子串一定可以看作是某兩個尾碼的公用首碼,第二,把題目轉化成去判定對於任意的一個長度k,是否存在長度至少為k的不重疊的重複的子串。