一個可以AK IOI的算法


暴力

很多問題都可以“暴力求解”,不用動太大腦筋,把所有可能性都列舉出來,然后一一試驗。雖然暴力求解法不用動太大大腦筋,但要注意時間復雜磁。因此,對問題一定的分析往往會讓算法更加簡潔高效。

例題

 

試題描述

輸入正整數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




5



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 ①

xbxa=2(xdxc)xb−xa=2(xd−xc) ②

xbxa<(xcxb)/3xb−xa<(xc−xb)/3 ③

現在魔改一下這三個式子

t=xdxct=xd−xc

所以②可化為xbxa=2txb−xa=2t ④

將④代入③

2t<(xcxb)/32t<(xc−xb)/3

移項一下,就變成

6t<xcxb6t<xc−xb ⑤

再魔改一下

6t+k=xcxb6t+k=xc−xb(就是把差的部分補上去)

於是可以畫出來一個圖

顯然,AA的最小值為11,DD的最大值為nn

由圖可得AD=9t+kAD=9t+k

所以我們可以嘗試着枚舉t,用t來表示各個魔法值的值

由上易得t的范圍為1<=t<=(n1)/91<=t<=(n−1)/9

在代碼中為了避免除法寫成t9<nt∗9<n

再枚舉D,因為我們已經枚舉出了t,所以C的值是可以直接算出來的

C=DtC=D−t

又因為使A,B,C,DA,B,C,D滿足條件的k的最小值為1,所以對於當前的C和D,最大的A和B為A=D9t1,B=D7t1A=D−9t−1,B=D−7t−1

那么如果A和B更小怎么辦?

觀察到在其他條件不變的情況下,只要CC和BB滿足XcXb>6tXc−Xb>6t,那么這個魔法陣就一定成立,所以當(a1<a2,b1<b2)(a1<a2,b1<b2)時,只要a2a2和b2b2能夠和C,DC,D組成魔法陣,a1,b1a1,b1也一定能和C,DC,D組成魔法陣,所以可以使用前綴和優化

然后又由乘法原理可得,當前魔法值作為DD物品的個數為SumD=SumASumBSumCSumD=SumA∗SumB∗SumC

所以我們利用前綴和優化SumASumBSumA∗SumB

C的情況可以順便在算D的時候算出來

那么還有一個問題是,我們枚舉的D的范圍是多少?

因為要統計前綴和,所以一定是要順推下去的,由上面那張圖我們可以知道,D的最大值為n,最小值則為當k=1且A=1的時候,所以D的最小值為9t+29∗t+2,再小是無法組成魔法陣的

同理可以枚舉A

但是這個的情況又和枚舉D的情況有一點不同

在其他條件不變的情況下,只要CC和BB滿足XcXb>6tXc−Xb>6t,那么這個魔法陣就一定成立,所以當(c1<c2,d1<d2)(c1<c2,d1<d2)時,只要c1c1和d1d1能夠和A,BA,B組成魔法陣,c2,d2c2,d2也一定能和A,BA,B組成魔法陣,所以可以使用后綴和優化

因為需要統計后綴和,所以需要逆推

枚舉的范圍:A的最大值為(nt91)(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;
}

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM