暴力
很多問題都可以“暴力求解”,不用動太大腦筋,把所有可能性都列舉出來,然后一一試驗。雖然暴力求解法不用動太大大腦筋,但要注意時間復雜磁。因此,對問題一定的分析往往會讓算法更加簡潔高效。
例題
試題描述
|
輸入正整數n,按從小到大順序輸出所有形如 abcde / fghij = n 的表達式,其中a - j恰好為數字0-9的一個排列(可以有前導0)2<=n <=79 |
輸入
|
n
|
輸出
|
abcde / fghij = n的表達式
|
輸入示例
|
62
|
輸出示例
|
79546 / 01283 = 62
94736 / 01528 = 62
|
分析:枚舉0 -9 所有 排列? 沒必要,我們只需要枚舉fghij 就可以算出abcde,然后判斷所有數字都不相同即可。代碼如下。
#include <bits/stdc++.h> using namespace std; int main() { int n, kase = 0; char buf[99]; while(scanf("%d", &n) == 1 && n) { int cnt = 0; if(kase++) printf("\n"); for(int fghij = 1234; ; fghij++) { int abcde = fghij * n;//題目規則 sprintf(buf, "%05d%05d", abcde, fghij);//格式化 if(strlen(buf) > 10) break;//長度超過10不符合 sort(buf, buf+10);//按順序排列10個數字 bool ok = true; for(int i = 0; i < 10; i++)//判斷是否都不相同 if(buf[i] != '0' + i) ok = false; if(ok) { cnt++; printf("%05d / %05d = %d\n", abcde, fghij, n); } } if(!cnt) printf("There are no solutions for %d.\n", n); } return 0; }
再來一道NOIP2016普及組第四題。
試題描述
|
六十年一次的魔法戰爭就要開始了,大魔法師准備從附近的魔法場中汲取魔法能量。 大魔法師有m個魔法物品,編號分別為1,2,...,m。每個物品具有一個魔法值,我們用Xi表示編號為i的物品的魔法值。每個魔法值Xi是不超過n的正整數,可能有多個物品的魔法值相同。 大魔法師認為,當且僅當四個編號為a,b,c,d的魔法物品滿足xa<xb<xc<xd,Xb-Xa=2(Xd-Xc),並且xb-xa<(xc-xb)/3時,這四個魔法物品形成了一個魔法陣,他稱這四個魔法物品分別為這個魔法陣的A物品,B物品,C物品,D物品。 現在,大魔法師想要知道,對於每個魔法物品,作為某個魔法陣的A物品出現的次數,作為B物品的次數,作為C物品的次數,和作為D物品的次數。 測試數據范圍: |
輸入
|
輸入文件的第一行包含兩個空格隔開的正整數n和m。
接下來m行,每行一個正整數,第i+1行的正整數表示Xi,即編號為i的物品的魔法值。 保證1≤n≤15000,1≤m≤40000,1≤xi≤n。每個Xi是分別在合法范圍內等概率隨機生成的。 |
輸出
|
共輸出m行,每行四個整數。第i行的四個整數依次表示編號為i的物品作 為A,B,C,D物品分別出現的次數。
保證標准輸出中的每個數都不會超過10^9。 每行相鄰的兩個數之間用恰好一個空格隔開。 |
輸入示例
|
【測試數據1】 30 8 1 24 7 28 5 29 26 24 【測試數據2】 15 15 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
輸出示例
|
【測試數據1】 4 0 0 0 0 0 1 0 0 2 0 0 0 0 1 1 1 3 0 0 0 0 0 2 0 0 2 2 0 0 1 0 【測試數據2】 5 0 0 0 4 0 0 0 3 5 0 0 2 4 0 0 1 3 0 0 0 2 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 2 1 0 0 3 2 0 0 4 3 0 0 5 4 0 0 0 5 |
其他說明
|
【樣例解釋1】
共有5個魔法陣,分別為: 物品1,3,7,6,其魔法值分別為1,7,26,29; 物品1,5,2,7,其魔法值分別為1,5,24,26; 物品1,5,7,4,其魔法值分別為1,5,26,28; 物品1,5,8,7,其魔法值分別為1,5,24,26; 物品5,3,4,6,其魔法值分別為5,7,28,29。 以物品5為例,它作為A物品出現了1次,作為B物品出現了3次,沒有作為C物品或者D物品出現,所以這一行輸出的四個數依次為1,3,0,0。 此外,如果我們將輸出看作一個m行4列的矩陣,那么每一列上的m個數之和都應等於魔法陣的總數。所以,如果你的輸出不滿足這個性質,那么這個輸出一定不正確。你可以通過這個性質在一定程度上檢查你的輸出的正確性。 【數據規模】見上 |
分析:這道題如果直接暴力,時間復雜度為O(n^4)。只能拿40分。所以,我們要做一個預處理。
首先可以發現每個xx都小於n,而nn最大值只是1500015000,所以可以開一個桶來存每個魔法值出現的次數
回憶一下3個約束條件
xa<xb<xc<xdxa<xb<xc<xd ①
xb−xa=2(xd−xc)xb−xa=2(xd−xc) ②
xb−xa<(xc−xb)/3xb−xa<(xc−xb)/3 ③
現在魔改一下這三個式子
設t=xd−xct=xd−xc
所以②可化為xb−xa=2txb−xa=2t ④
將④代入③
2t<(xc−xb)/32t<(xc−xb)/3
移項一下,就變成
6t<xc−xb6t<xc−xb ⑤
再魔改一下
設6t+k=xc−xb6t+k=xc−xb(就是把差的部分補上去)
於是可以畫出來一個圖
顯然,AA的最小值為11,DD的最大值為nn
由圖可得AD=9t+kAD=9t+k
所以我們可以嘗試着枚舉t,用t來表示各個魔法值的值
由上易得t的范圍為1<=t<=(n−1)/91<=t<=(n−1)/9
在代碼中為了避免除法寫成t∗9<nt∗9<n
再枚舉D,因為我們已經枚舉出了t,所以C的值是可以直接算出來的
C=D−tC=D−t
又因為使A,B,C,DA,B,C,D滿足條件的k的最小值為1,所以對於當前的C和D,最大的A和B為A=D−9t−1,B=D−7t−1A=D−9t−1,B=D−7t−1
那么如果A和B更小怎么辦?
觀察到在其他條件不變的情況下,只要CC和BB滿足Xc−Xb>6tXc−Xb>6t,那么這個魔法陣就一定成立,所以當(a1<a2,b1<b2)(a1<a2,b1<b2)時,只要a2a2和b2b2能夠和C,DC,D組成魔法陣,a1,b1a1,b1也一定能和C,DC,D組成魔法陣,所以可以使用前綴和優化
然后又由乘法原理可得,當前魔法值作為DD物品的個數為SumD=SumA∗SumB∗SumCSumD=SumA∗SumB∗SumC
所以我們利用前綴和優化SumA∗SumBSumA∗SumB
C的情況可以順便在算D的時候算出來
那么還有一個問題是,我們枚舉的D的范圍是多少?
因為要統計前綴和,所以一定是要順推下去的,由上面那張圖我們可以知道,D的最大值為n,最小值則為當k=1且A=1的時候,所以D的最小值為9∗t+29∗t+2,再小是無法組成魔法陣的
同理可以枚舉A
但是這個的情況又和枚舉D的情況有一點不同
在其他條件不變的情況下,只要CC和BB滿足Xc−Xb>6tXc−Xb>6t,那么這個魔法陣就一定成立,所以當(c1<c2,d1<d2)(c1<c2,d1<d2)時,只要c1c1和d1d1能夠和A,BA,B組成魔法陣,c2,d2c2,d2也一定能和A,BA,B組成魔法陣,所以可以使用后綴和優化
因為需要統計后綴和,所以需要逆推
枚舉的范圍:A的最大值為(n−t∗9−1)(n−t∗9−1)(因為當k=1,D=n的時候A才最大),A的最小值則為1
所以就可以算出每個魔法值作為A,B,C,DA,B,C,D物品的次數了,輸出時直接輸出當前魔法物品的魔法值的次數就可以了。代碼如下。
#include <cstdio> #include <algorithm> using namespace std; inline int read(){ int x=0,f=1;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-f;c=getchar();} while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();} return x*f; } #define N 50010 int n,m; int a[N],b[N],c[N],d[N]; int x[N],vis[N]; int main(){ n=read();m=read(); for(int i=1;i<=m;i++) x[i]=read(),vis[x[i]]++; for(int t=1;t*9<n;t++){ int sum=0; for(int D=9*t+2;D<=n;D++){ int A=D-9*t-1; int B=A+2*t; int C=D-t; sum+=vis[A]*vis[B]; c[C]+=vis[D]*sum; d[D]+=vis[C]*sum; } sum=0; for(int A=n-9*t-1;A;A--){ int B=A+2*t; int C=B+6*t+1; int D=A+9*t+1; sum+=vis[C]*vis[D]; a[A]+=vis[B]*sum; b[B]+=vis[A]*sum; } } for(int i=1;i<=m;i++){ printf("%d %d %d %d\n",a[x[i]],b[x[i]],c[x[i]],d[x[i]]); } return 0; }