歡迎評論指出錯誤,提出疑問,或者不介意給出更好的解法,有交流才有進步。
PREV-3(帶分數)
這道題就簡單的求1-9九個數組成的帶分數來表示數值n的個數
枚舉1-9九個數組成的全排列,然后把每種排列分成整數、分子、分母三段
然后簡單判斷以下每個帶分數是否和n相等
這里的剪枝在於枚舉整數、分子、分母分別的位數
分母的位數肯定小於等於分子的位數
整數的位數肯定小於等於n的位數
<C++> Code

1 #include<stdio.h> 2 3 int n,a[10],ans,len; 4 bool f[10];//標記是否已加入排列中 5 6 //計算a[]中從第s位起長l位的整數大小 7 int getNum(int s,int l){ 8 int num = 0; 9 for(int i = 0 ; i < l ; i++){ 10 num = num * 10 + a[s+i]; 11 } 12 return num; 13 } 14 15 //判斷全排列中是否有組成滿足條件的帶分數 16 void JudgeNum(){ 17 //現在枚舉帶分數 整數、分母、分子的位數 18 for(int zs = 1 ; zs <= len ; zs++){//整數的位數 19 int NumZS = getNum(0 , zs);//整數 20 int Len = 9 - zs;//分子加分母的位數 21 for(int fm = 1 ; fm <= Len/2 ; fm++){ 22 int NumFM = getNum(zs , fm);//分母 23 int fz = Len - fm; 24 int NumFZ = getNum(zs + fm , fz);//分子 25 if(NumFZ%NumFM == 0 && (NumZS + NumFZ/NumFM) == n) 26 ans++; 27 } 28 } 29 } 30 31 32 //dfs遍歷1-9組成的全排列 33 void dfs(int k){ 34 if(k == 9){//生成了一種排列 35 JudgeNum(); 36 return; 37 } 38 for(int i = 1 ; i < 10 ; i++){//枚舉第k位上的數 39 if(!f[i]){ 40 a[k] = i; 41 f[i] = true; 42 dfs(k+1); 43 f[i] = false; 44 } 45 } 46 } 47 48 49 void work(){ 50 //因為全局變量自動初始化為0(false),所以省了初始化 51 int x = n; 52 while(x){//求n的位數len 53 len++; 54 x /= 10; 55 } 56 dfs(0); 57 printf("%d\n",ans); 58 } 59 60 61 int main() 62 { 63 scanf("%d",&n); 64 work(); 65 return 0; 66 }
我覺得在getNum()上還可以優化一下
PREV-9(大臣的旅費)
題目給定n個城市,n-1條路,顯然題意是要在一棵樹上求任意兩點距離的最大值
/----------------------------------接下來兩段可忽略------------------------------------/
題目沒給定n的范圍,所以貿然用Floyd來求兩點間的最短距離是不可取的
Floyd的時間復雜度為O(n^3),n隨便取大一點就很容易超時
再者,這題不需要求兩點間的最短距離,因為任意兩點間的距離都是固定的(這個可以自己想想)
在一棵樹上求兩點的距離,或許會想到兩點的最近公共祖先,用算法LCA來求
求任意兩點的最近祖先,得查詢C(n,2)次,時間復雜度為O(n^2) 這樣仍然不能保證全過
聽說有一種將最近公共祖先轉換成RMQ問題的時間復雜度為O(nlogn)的在線算法(可自行度娘)
當然這題也不應該用關於兩點的最近公共祖先的算法來求,在n未知的情況下,時間復雜度還是太高了
/----------------------------------以下才是關鍵------------------------------------/
題目只要求最大值,完全可以用樹形動歸來,dfs遍歷一遍所有邊就好,時間復雜度O(n)
任意取一點作為根結點,dfs深搜,從葉子節點向上動歸
DP[i]維護以i為根節點的子樹中節點i到葉子節點的最長距離
MAX[i]維護以i為根節點的子樹中經過節點i的最大兩點間距離 答案自然是MAX[]中的最大值
<C++> Code

1 #include<cstdio> 2 #include<algorithm> 3 using namespace std; 4 #define MAXN 100010 //不知n為多大,隨便定義了個,可以定義更大,也可以想想用vector容器 5 #define LL long long 6 7 int n; 8 LL Dp[MAXN],Max[MAXN],ans;//全區變量自動初始化為0 9 10 //鏈式前向星 11 int head[MAXN],m=1;//因為head[]中元素都為0,所以m從1計數就不用初始化head[]了 12 struct Edge{ 13 int to,next,w; 14 }e[MAXN]; 15 16 //鏈式前向星添加邊 17 void add_edge(int u,int v,int w){ 18 e[m].to = v; 19 e[m].w = w; 20 e[m].next = head[u]; 21 head[u] = m++; 22 } 23 24 25 bool f[MAXN];//標記節點是否已被訪問過 26 void dfs(int s){ 27 int k = head[s]; 28 while(k > 0){ 29 int t = e[k].to;//t為s的孩子節點 30 if(!f[t]){ 31 f[t] = true; 32 dfs(t); 33 Max[s] = max(Max[s] , Dp[s] + Dp[t]+e[k].w);//以s為根節點的子樹中 經過s的最大兩點間距離 34 Dp[s] = max(Dp[s] , Dp[t]+e[k].w);//s到葉子節點的最長距離 35 } 36 k = e[k].next; 37 } 38 ans=max(ans,Max[s]); 39 } 40 41 42 void work(){ 43 f[1]=true; 44 dfs(1);//以節點1為根節點深搜 ,深搜前標記1被訪問 45 printf("%I64d\n",ans*(21+ans)/2); 46 } 47 48 49 void init(){ 50 scanf("%d",&n); 51 int p,q,d; 52 for(int i = 1 ; i < n ; i++){ 53 scanf("%d%d%d",&p,&q,&d); 54 add_edge(p,q,d); 55 add_edge(q,p,d);//雙向邊建圖,方便dfs 56 } 57 } 58 59 60 int main() 61 { 62 init(); 63 work(); 64 return 0; 65 }