NOIP2015提高組復賽Day1Day2詳解


T1:填幻方

這道題目非常水,直接按照題目里說的做就行了

其實機智的你們可以直接跳過題面直接開始做

 1 #include <cstdio>
 2 #include <cstring>
 3 int a[100][100];
 4 int main()
 5 {
 6     int n;
 7     memset(a,0,sizeof(a));
 8     scanf("%d",&n);
 9     int num=1;
10     int i,j;
11     i=1; j=n/2+1;
12     a[i][j]=1;
13     while (num<=n*n)
14     {
15         num++;
16         i--; j++;
17         if (i<1 && j<=n)
18         {
19             i=n;
20         }
21         else if (i<1 && j>n)
22         {
23             i++; i++; j--;
24         }
25         else if (j>n)
26         {
27             j=1;
28         }
29         else if (a[i][j]!=0)
30         {
31             i++;
32             j--;
33             i++;
34         }
35         a[i][j]=num;
36         /*for (int i=1; i<=n; i++)
37         {
38             for (int j=1; j<=n; j++)
39                 printf("%d ",a[i][j]);
40             printf("\n");
41         }*/
42     }
43     for (i=1;i<=n; i++)
44     {
45         for (j=1; j<=n-1; j++)
46             printf("%d ",a[i][j]);
47         printf("%d\n",a[i][n]);
48     }
49 }
View Code

T2:信息傳遞

題目描述

有 n個同學(編號為 1 到 n)正在玩一個信息傳遞的游戲。在游戲里每人都有一個
固定的信息傳遞對象,其中,編號為 i的同學的信息傳遞對象是編號為Ti的同學。 游戲開始時,每人都只知道自己的生日。之后每一輪中,所有人會同時將自己當前
所知的生日信息告訴各自的信息傳遞對象(注意:可能有人可以從若干人那里獲取信息, 但是每人只會把信息告訴一個人,即自己的信息傳遞對象)。當有人從別人口中得知自 己的生日時,游戲結束。請問該游戲一共可以進行幾輪?

輸入

輸入共 2 行。
第 1 行包含 1 個正整數第 1 行包含 1 個正整數 n,表示 n 個人。
第 2 行包含

輸出

輸出共 1 行,包含 1 個整數,表示游戲一共可以進行多少輪。

 

樣例輸入

5 2 4 2 3 1

樣例輸出

3
 
我們根據題目中的意思進行轉化
可以發現每一個點都只有一條出邊
那么我們只需要找圖中的最小環,並且求出它的長度就可以了
我們可以先進行一次拓撲排序,然后圖中剩下的點構成的就是環了
然后我可以進行暴力枚舉
如果一個點的入度不為零,並且它還未被訪問過
那么我們就從這個節點開始暴力求這個環的長度
每找到一個環,我們更新最后的答案A
 1 #include <cstdio>
 2 #include <queue>
 3 #include <cstring>
 4 using namespace std;
 5 queue <int> q;
 6 int f[200001]; int flag[200001]; int e[200001];
 7 int main()
 8 {
 9     memset(flag,0,sizeof(flag));
10     int n;
11     scanf("%d",&n);
12     for (int i=1; i<=n; i++)
13     {
14         scanf("%d",&f[i]);
15         e[f[i]]++;
16     }
17     for (int i=1; i<=n; i++)
18     {
19         if (e[i]==0)
20         {
21             q.push(i);
22             flag[i]=1;
23         }   
24     }
25     while (! q.empty())
26     {
27         int now=q.front();
28         q.pop();
29         --e[f[now]];
30         if (e[f[now]]==0)
31         {
32             flag[f[now]]=1;
33             q.push(f[now]);
34         }
35     }
36     int ans=1000000000;
37     for (int i=1; i<=n; i++)
38     {
39         if (e[i] !=0 && flag[i]==0)
40         {
41             flag[i]=1;
42             int j=f[i];
43             int tmp=1;
44             while (flag[j]==0)
45             {
46                 flag[j]=1;
47                 j=f[j];
48                 tmp++;
49             }
50             if (tmp<=ans) ans=tmp;
51         }
52     }
53     printf("%d\n",ans);
54     return 0;
55 }
View Code

 

T3:斗地主

輸入

第一行包含用空格隔開的2個正整數 T,n ,表示手牌的組數以及每組手牌的張數。

接下來 T 組數據,每組數據 n 行,每行一個非負整數對 ai,bi ,表示一張牌,其中 ai 表示牌的數碼, bi 表示牌的花色,中間用空格隔開。特別的,我們用 1 來表示數碼 A, 11 表示數碼 J, 12 表示數碼 Q, 13 表示數碼 K;黑桃、紅心、梅花、方片分別用 1-4 來表示;小王的表示方法為 0 1 ,大王的表示方法為 0 2 。

輸出

共 T 行,每行一個整數,表示打光第

樣例輸入

1 8

7 4

8 4

9 1

10 4

11 1

5 1

1 4

1 1

樣例輸出

3
 
這道題目看似非常的難
但是我們這里有一個基本的貪心策略
就是我們優先出牌數多的那些出牌順序
因為如果存在一個三順子,你將它拆開來打和合起來打效果其實是一樣的,因為你減少的牌的總量以及類別是相同的
所以我們寧可合起來,也不要分開來打
有了上面這個結論我們就可以輕而易舉地寫出代碼了
我們可以寫個大暴力加最優性剪枝優化
  1 #include <cmath>
  2 #include <cstdio>
  3 #include <cstring>
  4 #include <iostream>
  5 #include <algorithm>
  6 #define ll long long
  7 #define fo(i,x,y) for(int i=x; i<=y; i++)
  8 #define pr(i,x,y) for(int i=x; i>=y; i--)
  9 #define clear(a,x) memset(a,x,sizeof(a))
 10 #define INF 1e15
 11 #define EPS 1e-8
 12 
 13 using namespace std;
 14 
 15 int t,N,x,y,a[5],b[14],MaxX;
 16 
 17 inline ll read()
 18 {
 19     int f=1;
 20     ll Tmp=0;
 21     char ch=getchar();
 22     while (ch != '-' && ch < '0' || ch > '9')
 23     {
 24         ch=getchar();
 25     }
 26     if (ch == '-')
 27     {
 28         f=-1;
 29         ch=getchar();
 30     }
 31     while (ch >= '0' && ch <= '9')
 32     {
 33         Tmp=Tmp * 10 + ch - 48;
 34         ch=getchar();
 35     }
 36     return Tmp * f;
 37 }
 38 
 39 
 40 int qiu()
 41 {
 42     int tot=0;
 43     memset(a,0,sizeof(a));
 44     for(int i=0;i<=13;i++)
 45     {
 46         if (i) a[b[i]]++;
 47     }
 48     while(a[4] && a[2]>1) a[4]--,a[2]-=2,tot++;
 49     while(a[4] && a[1]>1) a[4]--,a[1]-=2,tot++;
 50     while(a[4] && a[2]) a[4]--,a[2]--,tot++;
 51     while(a[3] && a[2]) a[3]--,a[2]--,tot++;
 52     while(a[3] && a[1]) a[3]--,a[1]--,tot++;
 53     return tot+a[1]+a[2]+a[3]+a[4];
 54 }
 55 
 56 void dfs(int u)
 57 {
 58     if(u>=MaxX) return;int kk=qiu();
 59     if(u+kk<MaxX) MaxX=u+kk;
 60     for(int i=2;i<=13;i++)
 61     {
 62         int j=i;
 63         while(b[j]>=3 && j<=13) j++;
 64         if(j-i>=2)
 65           for(int v=i+1;v<=j-1;v++)
 66           {
 67 
 68             for(int vk=i;vk<=v;vk++) b[vk]-=3;
 69             dfs(u+1);
 70             for(int vk=i;vk<=v;vk++) b[vk]+=3;
 71           }
 72     }
 73     for(int i=2;i<=13;i++)
 74     {
 75         int j=i;
 76         while(b[j]>=2 && j<=13) j++;
 77         if(j-i>=3)
 78           for(int v=i+2;v<=j-1;v++)
 79           {
 80             for(int vk=i;vk<=v;vk++) b[vk]-=2;
 81             dfs(u+1);
 82             for(int vk=i;vk<=v;vk++) b[vk]+=2;
 83           }
 84     }
 85     for(int i=2;i<=13;i++)
 86     {
 87         int j=i;
 88         while(b[j]>=1 && j<=13) j++;
 89         if(j-i>=5)
 90           for(int v=i+4;v<=j-1;v++)
 91           {
 92             for(int vk=i;vk<=v;vk++) b[vk]--;
 93             dfs(u+1);
 94             for(int vk=i;vk<=v;vk++) b[vk]++;
 95           }
 96     }
 97 }
 98 
 99 int main()
100 {
101     int _=read();
102     N=read();
103     while (_--)
104     {
105         MaxX=2147483647;
106         clear(b,0);
107         fo(i,1,N)
108         {
109             int X,Y;
110             X=read(); Y=read();
111             if (X == 1)
112             {
113 
114                 X=13;
115             }
116             else
117             {
118                 if (X)
119                 {
120                     X--;
121                 }
122             }
123             b[X]++;
124         }
125         dfs(0);
126         printf("%d\n",MaxX);
127     }
128     return 0;
129 }
View Code

 

 

Day2開始啦

T1:跳石頭

題目描述

一年一度的“跳石頭”比賽又要開始了! 這項比賽將在一條筆直的河道中進行,河道中分布着一些巨大岩石。組委會已經選
擇好了兩塊岩石作為比賽起點和終點。在起點和終點之間,有 N 塊岩石(不含起點和終 點的岩石)。在比賽過程中,選手們將從起點出發,每一步跳向相鄰的岩石,直至到達 終點。
為了提高比賽難度,組委會計划移走一些岩石,使得選手們在比賽過程中的最短跳 躍距離盡可能長。由於預算限制,組委會至多從起點和終點之間移走 M 塊岩石(不能 移走起點和終點的岩石)。

 

輸入

輸入文件第一行包含三個整數 L,N,M,分別表示起點到終點的距離,起點和終 點之間的岩石數,以及組委會至多移走的岩石數。
接下來 N 行,每行一個整數,第 i 行的整數 Di(0 < Di < L)表示第 i 塊岩石與 起點的距離。這些岩石按與起點距離從小到大的順序給出,且不會有兩個岩石出現在同 一個位置。

 

輸出

輸出文件只包含一個整數,即最短跳躍距離的最大值。

 

樣例輸入

25 5 2
2
1 1
1 4
17
21

樣例輸出

4

提示

 

【輸入輸出樣例 1 說明】

將與起點距離為 2 和 14 的兩個岩石移走后,最短的跳躍距離為 4(從與起點距離 17 的岩石跳到距離 21 的岩石,或者從距離 21 的岩石跳到終點)。


【數據規模與約定】

對於 20%的數據,0 ≤ M ≤ N ≤ 10。對於50%的數據,0 ≤ M ≤ N ≤ 100。

對於 100%的數據,0 ≤ M ≤ N ≤ 50,000,1 ≤ L ≤ 1,000,000,000。

 

這道題目我們開始研究這個最終的答案是否具有二分性

如果X可行,那么X+1一定可行

如果X不可行,那么X-1一定不可行

所以我們就可以進行二分答案了

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <iostream>
 4 #include <algorithm>
 5  
 6 using namespace std;
 7  
 8 int n,k;
 9 int a[500005];
10  
11 bool check(int x)
12 {
13     int tmp=0;int h=0;
14     for (int i=1; i<=n; i++)
15     {
16         if (a[i]-h < x)
17         {
18             tmp++;
19         }
20         else h=a[i];
21     }
22     if (tmp <= k) return true;
23     else return false;
24 }
25  
26 int main()
27 {
28     a[0]=0;
29     int L;
30     scanf("%d%d%d",&L,&n,&k);
31     for (int i=1; i<=n; i++)
32         scanf("%d",&a[i]);
33     a[++n]=L;
34     int l=0,r=1000000000;
35     while (l<r)
36     {
37         //printf("%d %d\n",l,r);
38         int mid=(l+r+1)/2;
39         if (check(mid)) l=mid;
40         else r=mid-1;
41     }
42     printf("%d",l);
43 }
View Code

T2:子串

題目描述

有兩個僅包含小寫英文字母的字符串 A 和 B。現在要從字符串 A 中取出 k 個互不重疊的非空子串,然后把這 k 個子串按照其在字符串 A 中出現的順序依次連接起來得到一 個新的字符串,請問有多少種方案可以使得這個新串與字符串 B 相等?注意:子串取出 的位置不同也認為是不同的方案。

 

輸入

第一行是三個正整數 n,m,k,分別表示字符串 A 的長度,字符串 B 的長度,以及問題描述中所提到的 k,每兩個整數之間用一個空格隔開。 第二行包含一個長度為 n 的字符串,表示字符串 A。 第三行包含一個長度為 m 的字符串,表示字符串 B。

 

輸出

輸出共一行,包含一個整數,表示所求方案數。由於答案可能很大,所以這里要求輸出答案對 1,000,000,007 取模的結果。

 

樣例輸入

6 3 1
aabaab
aab
6 3 2
aabaab
aab

樣例輸出

2
7
 
這道題目很明顯是一個DP 題
我們可以知道與當前答案有關聯的只有A的第i個字符,B的第j個字符,已經取了k個字符
那么設f[i][j][kk]表示在A串的前i個字符中選kk個子串匹配B串的前j個字符的方案數.求方案數可以采用加法原理,考慮A串的第i個字符,那么這個字符的決策只有取或不取,很明顯,加法原理,把不取的方案數和取的方案數加起來就可以,但是狀態的定義並不能看出這個字符到底取不取,或者說並不能推出結果來,怎么辦呢?

 

那么就用一個數組s[i][j][kk]來表示在A串的前i個字符中選kk個子串匹配B串的前j個字符的方案數,A串的第i個字符會被取到.那么這個s數組該怎么推出來呢?可以發現,如果取第i個字符也有2種可能,因為kk是一定的,第i個字符可能和第i-1個字符合並成一個子串,那么從s[i-1][j][kk]轉移過來,也可能不和第i-1個字符合並成一個子串,那么就要新開一個子串,故kk一定從kk-1轉移過來,根據加法原理,那么s[i][j][kk] = s[i-1][j-1][kk] + f[i-1][j-1][kk-1].

還有一個問題:這是一個三維的狀態轉移方程!空間不一定開的下,再看數據范圍,這絕對MLE,怎么辦?注意到i只能從i或i-1轉移過來,可以想到滾動數組

此處題解參考自某位大

 

 1 #include <cmath>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <iostream>
 5 #include <algorithm>
 6 
 7 using namespace std;
 8 
 9 int N,M,K;
10 int Mod1=1e9 + 7;
11 char s1[100005];
12 char s2[100005];
13 int dp[2][205][205];
14 int S[2][205][205];
15 
16 int main()
17 {
18     scanf("%d%d%d",&N,&M,&K);
19     scanf("%s",s1+1);
20     scanf("%s",s2+1);
21     S[0][0][0]=1;
22     for (int i=1; i<=N; i++)
23     {
24         int Now=i & 1;
25         int Last=Now ^ 1;
26         S[Now][0][0]=1;
27         for (int j=1; j<=M; j++)
28         {
29             for (int k=1; k<=K; k++)
30             {
31                 if (s1[i] == s2[j])
32                 {
33                     dp[Now][j][k]=(dp[Last][j - 1][k] + S[Last][j - 1][k-1]) % Mod1;
34                 }
35                 else dp[Now][j][k]=0;
36                 S[Now][j][k]=(S[Last][j][k] + dp[Now][j][k]) % Mod1;
37             }
38         }
39     }
40     printf("%d\n",S[N & 1][M][K]);
41     return 0;
42 }
View Code

 

T3:運輸計划

題目描述

公元 2044 年,人類進入了宇宙紀元。
L 國有 n 個星球,還有 n-1 條雙向航道,每條航道建立在兩個星球之間,這 n-1 條
航道連通了 L 國的所有星球。
小 P 掌管一家物流公司,該公司有很多個運輸計划,每個運輸計划形如:有一艘物
流飛船需要從 ui 號星球沿最快的宇航路徑飛行到 vi 號星球去。顯然,飛船駛過一條航道 是需要時間的,對於航道 j,任意飛船駛過它所花費的時間為 tj,並且任意兩艘飛船之 間不會產生任何干擾。
為了鼓勵科技創新,L 國國王同意小 P 的物流公司參與 L 國的航道建設,即允許小 P 把某一條航道改造成蟲洞,飛船駛過蟲洞不消耗時間。
在蟲洞的建設完成前小 P 的物流公司就預接了 m 個運輸計划。在蟲洞建設完成后, 這 m 個運輸計划會同時開始,所有飛船一起出發。當這 m 個運輸計划都完成時,小 P 的 物流公司的階段性工作就完成了。
如果小 P 可以自由選擇將哪一條航道改造成蟲洞,試求出小 P 的物流公司完成階段 性工作所需要的最短時間是多少?

 

輸入

第一行包括兩個正整數 n、m,表示 L 國中星球的數量及小 P 公司預接的運輸計划的
數量,星球從 1 到 n 編號。
接下來 n-1 行描述航道的建設情況,其中第 i 行包含三個整數 ai, bi 和 ti,表示第
i 條雙向航道修建在 ai 與 bi 兩個星球之間,任意飛船駛過它所花費的時間為 ti。 接下來 m 行描述運輸計划的情況,其中第 j 行包含兩個正整數 uj 和 vj,表示第 j 個
運輸計划是從 uj 號星球飛往 vj 號星球。 

 

輸出

共 1 行,包含 1 個整數,表示小 P 的物流公司完成階段性工作所需要的最短時間。 
首先求出每個計划的路徑長度 這里寫的倍增
然后二分答案
對於每個ans 統計>他的路徑條數 tot 並維護最大差值 dec 
並且對於每條不合法的路徑維護每個點的經過次數
然后枚舉點 如果經過次數==tot說明每一條不合法的都經過他
然后嘗試把它建成蟲洞 如果他對應邊的權值>=dec 那么我們刪掉它ans就合法了
關鍵是統計每個點在非法路徑中的經過次數 :
維護sum數組 對於每個非法的路徑起點a b LCA(a,b)==s sum[a]++ sum[b]++ sum[s]-=2
這樣網上更新的話 經過的點的sum值都變成1 祖先s的變成0 
這樣就實現了sum數組的維護 

參考自某位大佬

  1 #include <cstdio>
  2 #include <cstring>
  3 #include <iostream>
  4 #include <algorithm>
  5  
  6 using namespace std;
  7  
  8 bool flag[1000005],flag1[1000005];
  9 int num,n,m;
 10 int fa[300005][30];
 11 long long head[600005],sum[600005],dis[600005],L[600005],R[600005],p[600005],next[600005],father[600005],father1[600005],vet[600005],val[600005];
 12 long long dep[600005],dep1[600005];
 13  
 14 void add(int u,int v,int cost)
 15 {
 16     vet[++num]=v;
 17     val[num]=cost;
 18     next[num]=head[u];
 19     head[u]=num;
 20 }
 21  
 22 void dfs(int u)
 23 {
 24     flag1[u]=true;
 25     int len=1;
 26     while ((1 <<len) <= dep[u])
 27     {
 28         fa[u][len]=fa[fa[u][len-1]][len-1];
 29         ++len;
 30     }
 31     int i=head[u];
 32     while (i != -1)
 33     {
 34         if (vet[i] != fa[u][0])
 35         {
 36             dep[vet[i]]=dep[u]+1;
 37             fa[vet[i]][0]=u;
 38             father[vet[i]]=u;
 39             father1[vet[i]]=val[i];
 40             dep1[vet[i]]=dep1[u]+val[i];
 41             dfs(vet[i]);
 42         }
 43         i=next[i];
 44     }
 45 }
 46  
 47 int lca(int x,int y)
 48 {
 49     if (dep[x] < dep[y])
 50     {
 51         swap(x,y);
 52     }
 53     for (int i=20; i>=0; i--)
 54     {
 55         if (dep[x] - dep[y] >= (1 << i))
 56         {
 57             x=fa[x][i];
 58         }
 59     }
 60     if (x == y) return x;
 61     for (int i=20; i>=0; i--)
 62     {
 63         if (fa[x][i] != fa[y][i])
 64         {
 65             x=fa[x][i];
 66             y=fa[y][i];
 67         }
 68     }
 69     return fa[x][0];
 70 }
 71  
 72 void dfs1(int u)
 73 {
 74     flag[u]=true;
 75     for (int i=head[u]; i!=-1; i=next[i])
 76     {
 77         if (flag[vet[i]] == false)
 78         {
 79             dfs1(vet[i]);
 80             sum[u]=sum[u]+sum[vet[i]];
 81         }
 82     }
 83 }
 84  
 85 bool check(int x)
 86 {
 87     int ans=0;
 88     for (int i=1; i<=n; i++)
 89     {
 90         sum[i]=0;
 91     }
 92     long long maxll=0;
 93     for (int i=1; i<=m; i++)
 94     {
 95         if (dis[i] <= x) continue;
 96         else
 97         {
 98             sum[L[i]]++;
 99             sum[R[i]]++;
100             sum[p[i]]-=2;
101             ans++;
102             maxll=max(dis[i],maxll);
103         }
104     }
105     dfs1(1);
106     long long maxx=0;
107     for (int i=1; i<=n; i++)
108     {
109         if (sum[i] == ans)
110         {
111             maxx=max(maxx,father1[i]);
112         }
113     }
114     for (int i=1; i<=n; i++)
115         flag[i]=false;
116     for (int i=1; i<=n; i++)
117     {
118         sum[i]=0;
119     }
120     if (maxll - maxx <= x) return true;
121     else return false;
122 }
123  
124 int find(int l,int r)
125 {
126     while (l < r)
127     {
128         int mid=(l + r) / 2;
129         //printf("%d\n",mid);
130         if (check(mid)) r=mid;
131         else l=mid+1;
132     }
133     return l;
134 }
135  
136 int main()
137 {
138     num=0;
139     scanf("%d%d",&n,&m);
140     for (int i=1; i<=n; i++)
141         head[i]=-1;
142     for (int i=1; i<n; i++)
143     {
144         int u,v,cost;
145         scanf("%d%d%d",&u,&v,&cost);
146         add(u,v,cost);
147         add(v,u,cost);
148     }
149     memset(flag1,false,sizeof(flag1));
150     dep1[1]=0;
151     dfs(1);
152     int max1=0;
153     for (int i=1; i<=m; i++)
154     {
155         scanf("%d%d",&L[i],&R[i]);
156         p[i]=lca(L[i],R[i]);
157         dis[i]=dep1[L[i]]+dep1[R[i]]-2*dep1[p[i]];
158         max1=max(max1,(int) dis[i]);
159     }
160     //for (int i=1; i<=m; i++)
161     //    printf("%d\n",dis[i]);
162     int XXX=find(0,max1);
163     printf("%d\n",XXX);
164 }
View Code

 

最后我想說兩句話

Hala Madrid!

李沁么么噠


免責聲明!

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



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