『NOIP2018PJ題解』
<更新提示>更新提示>
<第一次更新>第一次更新>
<正文>正文>
標題統計
題目描述
凱凱剛寫了一篇美妙的作文,請問這篇作文的標題中有多少個字元? 注意:標題中可能包含大、小寫英文字母、數字字元、空格和換行符。統計標題字 符數時,空格和換行符不計算在內。
輸入格式
輸入檔案只有一行,一個字串 s。
輸出格式
輸出檔案只有一行,包含一個整數,即作文標題的字元數(不含空格和換行符)。
樣例資料
input1
234
output1
3
input2
Ca 45
output2
4
樣例說明
樣例 1 :標題中共有 3 個字元,這 3 個字元都是數字字元。
樣例 2 :標題中共有 5 個字元,包括 1 個大寫英文字母, 1 個小寫英文字母和 2 個數字字元, 還有 1 個空格。由於空格不計入結果中,故標題的有效字元數為 4 個。
資料規模與約定
規定∣s∣ 表示字串 s 的長度(即字串中的字元和空格數)。
對於 %40 的資料,1 ≤ |s| ≤ 5,保證輸入為數字字元及行末換行符。
對於 %80 的資料,1 ≤ |s| ≤ 5,輸入只可能包含大、小寫英文字母、數字字元及行末換行符。
對於 %100 的資料,1 ≤ |s| ≤ 5,輸入可能包含大、小寫英文字母、數字字元、空格和行末換行符。
時間限制:1s
空間限制:256MB
解析
T1和往年一樣,還是簽到題。不過這一次好像更注重考察語言了,不少不熟悉語言的小夥伴可能就沒有分了啦。
大概是考察如何處理輸入吧,會用\(getline()\)的基本都用了\(getline()\)
\(Code:\)
#include<bits/stdc++.h> using namespace std; string s;int ans=0; int main(void) { freopen("title.in","r",stdin); freopen("title.out","w",stdout); getline(cin,s); for(int i=0;i<s.size();i++) { if(s[i]!=' ')ans++; } printf("%d\n",ans); return 0; }
#include<bits/stdc++.h>
using namespace std;
char s;
int ans=0;
int main()
{
freopen("title.in","r",stdin);
freopen("title.out","w",stdout);
while (cin>>s)
{
if (s>='0'&&s<='9'||s>='a'&&s<='z'||s>='A'&&s<='Z') ans++;
}
cout<<ans<<endl;
return 0;
}
龍虎鬥
題目描述
軒軒和凱凱正在玩一款叫《龍虎鬥》的遊戲,遊戲的棋盤是一條線段,線段上有 n個兵營(自左至右編號 1 ∼n),相鄰編號的兵營之間相隔 1 釐米,即棋盤為長度為 n-1 釐米的線段。i 號兵營裡有 ci位工兵。
下面圖 1 為 n=6的示例:
軒軒在左側,代表“龍”;凱凱在右側,代表“虎”。 他們以 m 號兵營作為分界, 靠左的工兵屬於龍勢力,靠右的工兵屬於虎勢力,而第 m號兵營中的工兵很糾結,他們不屬於任何一方。
一個兵營的氣勢為:該兵營中的工兵數 × 該兵營到 m 號兵營的距離;參與遊戲 一方的勢力定義為:屬於這一方所有兵營的氣勢之和。
下面圖 2 為 n = 6,m = 4 的示例,其中紅色為龍方,黃色為虎方:
遊戲過程中,某一刻天降神兵,共有 s1位工兵突然出現在了 p1號兵營。作為軒軒和凱凱的朋友,你知道如果龍虎雙方氣勢差距太懸殊,軒軒和凱凱就不願意繼續玩下去了。為了讓遊戲繼續,你需要選擇一個兵營 p2,並將你手裡的 s2位工兵全部派往 兵營 p2,使得雙方氣勢差距儘可能小。
注意:你手中的工兵落在哪個兵營,就和該兵營中其他工兵有相同的勢力歸屬(如果落在 m 號兵營,則不屬於任何勢力)。
輸入格式
輸入檔案的第一行包含一個正整數n,代表兵營的數量。
接下來的一行包含 n 個正整數,相鄰兩數之間以一個空格分隔,第 i個正整數代 表編號為 i 的兵營中起始時的工兵數量 ci。
接下來的一行包含四個正整數,相鄰兩數間以一個空格分隔,分別代表 m,p1,s1,s2。
輸出格式
輸出檔案有一行,包含一個正整數,即 p2,表示你選擇的兵營編號。如果存在多個編號同時滿足最優,取最小的編號。
樣例資料
input1
6
2 3 2 3 2 3
4 6 5 2
output1
2
input2
6
1 1 1 1 1 16
5 4 1 1
output2
1
樣例說明
樣例1:見問題描述中的圖 2。
雙方以 m=4號兵營分界,有 s1=5位工兵突然出現在p1=6號兵營。 龍方的氣勢為:
2×(4−1)+3×(4−2)+2×(4−3)=14
虎方的氣勢為:
2×(5−4)+(3+5)×(6−4)=18
當你將手中的 s2=2s2=2位工兵派往 p2=2號兵營時,龍方的氣勢變為:
14+2×(4−2)=18
此時雙方氣勢相等。
樣例2:
雙方以 m = 5號兵營分界,有 s1=1位工兵突然出現在 p1=4號兵營。
龍方的氣勢為:
1×(5−1)+1×(5−2)+1×(5−3)+(1+1)×(5−4)=11
虎方的氣勢為:
16×(6−5)=16
當你將手中的 s2=1位工兵派往 p2=1號兵營時,龍方的氣勢變為:
11+1×(5−1)=15
此時可以使雙方氣勢的差距最小。
資料規模與約定
1<m<n,1≤p1≤n。
對於%20 的資料,\(n=3,m=2,ci=1,s1,s2≤100。\)
另有%20 的資料,\(n≤10,p1=m,ci=1,s1,s2≤100。\)
對於%60 的資料,\(n≤100,ci=1,s1,s2≤100。\)
對於%80 的資料,\(n≤100,ci,s1,s2≤100。\)
對於%100 的資料,\(n≤10^5,ci,s1,s2≤10^9。\)
時間限制:1s
空間限制:256MB
解析
今年的第二題沒有往年怎麼簡單啦。這回T2雖然說也沒有考演算法,但是細節問題就很多了,於是就有了不少坑點:
1.爆int
2.min的初值需要賦到MAX_LONGLONG大小
3.需要注意把工兵放在m號兵營的情況
那麼大體思路是這樣的,我們暴力列舉把\(s2\)個工兵放在每一個兵營的情況,看看放在哪裡呢得到最優解,直接輸出即可。那麼,這裡就有一個顯而易見的優化了:那一邊初始的氣勢值總和最小,那就在那一邊列舉。但是,這就如3.,無論是那一邊,我們都要判斷是否把\(s2\)個工兵放在\(m\)號兵營會取得更優的答案,因為存在如下一種情況:
一開始雙方勢力值總和的差較小,我們在勢力值總和較小的一邊列舉,但是\(s2\)的值巨大,無論放在哪裡,新勢力值的差比原勢力值的差還大,此時,不如把工兵放在\(m\)號兵營處,讓勢力值總和不變化
如果這些都注意到了,那麼這題就解決了。
#include<bits/stdc++.h>
using namespace std;
inline void read(long long &k)
{
long long x=0,w=0;char ch;
while(!isdigit(ch))w|=ch=='-',ch=getchar();
while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
k=(w?-x:x);return;
}
const int Maxn=100000+80;
long long n,c[Maxn],m,p1,s1,s2,vleft=0,vright=0,Mindif=0x3f3f3f3f,ans;
inline void input(void)
{
read(n);
for(int i=1;i<=n;i++)read(c[i]);
read(m),read(p1);
read(s1),read(s2);
c[p1]+=s1;
}
inline void init(void)
{
for(int i=1;i<m;i++)vleft+=(c[i]*(m-i));
for(int i=m+1;i<=n;i++)vright+=(c[i]*(i-m));
}
inline void work(void)
{
if(vleft<vright)
{
for(int i=1;i<=m;i++)
{
long long l=vleft+s2*(m-i);
long long dif=abs(vright-l);
if(dif<Mindif)
{
Mindif=dif;
ans=i;
}
}
}
else if(vright<vleft)
{
for(int i=m;i<=n;i++)
{
long long r=vright+(i-m)*s2;
long long dif=abs(r-vleft);
if(dif<Mindif){
Mindif=dif;
ans=i;
}
}
}
else
{
ans=m;
}
}
int main(void)
{
freopen("fight.in","r",stdin);
freopen("fight.out","w",stdout);
input();
init();
work();
printf("%lld\n",ans);
return 0;
}
擺渡車
題目描述
有 n 名同學要乘坐擺渡車從人大附中前往人民大學,第 i 位同學在第 ti分鐘去 等車。只有一輛擺渡車在工作,但擺渡車容量可以視為無限大。擺渡車從人大附中出發、 把車上的同學送到人民大學、再回到人大附中(去接其他同學),這樣往返一趟總共花費m分鐘(同學上下車時間忽略不計)。擺渡車要將所有同學都送到人民大學。
凱凱很好奇,如果他能任意安排擺渡車出發的時間,那麼這些同學的等車時間之和最小為多少呢?
注意:擺渡車回到人大附中後可以即刻出發。
輸入格式
第一行包含兩個正整數 n,m,以一個空格分開,分別代表等車人數和擺渡車往返 一趟的時間。
第二行包含 n 個正整數,相鄰兩數之間以一個空格分隔,第 i 個非負整數 ti代 表第 i 個同學到達車站的時刻。
輸出格式
輸出一行,一個整數,表示所有同學等車時間之和的最小值(單位:分鐘)。
樣例資料
input1
5 1
3 4 4 3 5
output1
0
input2
5 5
11 13 1 5 5
output2
4
樣例說明
樣例1:
同學 1 和同學 4 在第 3 分鐘開始等車,等待 0 分鐘,在第 3 分鐘乘坐擺渡車出發。擺渡車在第 4分鐘回到人大附中。
同學 2 和同學 3 在第 4 分鐘開始等車,等待 0 分鐘,在第 4 分鐘乘坐擺渡車 出發。擺渡車在第 5 分鐘回到人大附中。
同學 5 在第 5 分鐘開始等車,等待 0 分鐘,在第 5 分鐘乘坐擺渡車出發。自此 所有同學都被送到人民大學。總等待時間為 0。
樣例2:
同學 3 在第 1 分鐘開始等車,等待 0 分鐘,在第 1 分鐘乘坐擺渡車出發。擺渡 車在第 6分鐘回到人大附中。
同學 4 和同學 5 在第 5 分鐘開始等車,等待 1 分鐘,在第 6 分鐘乘坐擺渡車 出發。擺渡車在第 11 分鐘回到人大附中。
同學 1 在第 11 分鐘開始等車,等待 2 分鐘;同學 2 在第 13 分鐘開始等車, 等待 0 分鐘。他/她們在第 13 分鐘乘坐擺渡車出發。自此所有同學都被送到人民大學。 總等待時間為 4。
可以證明,沒有總等待時間小於 4 的方案。
資料規模與約定
對於%10 的資料,\(n≤10,m=1,0≤ti≤100。\)
對於%30 的資料,\(n≤20,m≤2,0≤ti≤100。\)
對於%50 的資料,\(n≤500,m≤100,0≤ti≤10^4。\)
另有%20 的資料,\(n≤500,m≤10,0≤ti≤4×10^6。\)
對於%100 的資料,\(n≤500,m≤100,0≤ti≤4×10^6。\)
時間限制:2s
空間限制:256MB
題解
這就是今年的毒瘤T3了,可以說是近幾年來最難的一道題,比今年T4還難。
可是這道題的題面竟然異常簡潔,讓人一看就懂。有沒有人像我一樣一看就覺得簡單的(`・ω・´),然後就死在這了。
首先按時間排序,這是一定的。最容易讓人想到的就是\(O(n^3)\)的\(dp\)了,設\(f[i]\)代表前\(i\)個同學到達的最小等待時間總和,那麼暴力轉移是不會超時的。但是考場上怎麼都不會想到的是,這樣的dp在轉移時的花費是會計算錯誤的,而且小資料基本調不出來,主要原因是:我們不知道在轉移第\(i\)位同學時車是什麼時候到的,也就是不知道轉移的出發時間。所以在大資料上,一維的\(dp\)就基本全錯了。
但是這個錯誤演算法也給我們一個啟示,我們就可以藉此來改進dp了。設\(f[i][j]\)代表前i個同學,第\(i\)個同學在第\(t[i]+j\)分鐘到達的最小等待時間總和,這樣的狀態就不會出錯。其實,這樣設定後我們可以很簡單地劃分階段,\(f[i][j]\)不需要從\(f[k][l]\)轉移,只需要從\(f[i-1][k]\)轉移即可,因為\(i\)個同學必然屬於以下兩類中的一類:
1.第\(i\)個同學和第\(i-1\)個同學坐同一輛車
2.第\(i\)個同學和第\(i-1\)個同學不坐同一輛車
那麼我們來討論轉移的細節。
先看一個最基本的問題,兩位到達時間差超過\(m\)且到達時間相鄰的同學必然不坐同一輛車,因為他們的時間間隔足以讓車往返一趟。
那麼狀態第二維的上限只需要開到\(2m\)大小即可,達到上限的情況就是和他同時來的一個同學直接坐車走了,花費m分鐘,車回來後再送他,又花費m分鐘,在第\(t[i]+2m\)分鐘時,前i位同學全部到達。
此時,我們利用階段轉移:
\[ 1.\ if(t[i-1]+k==t[i]+j)\ f[i][j]=min(f[i][j],f[i-1][k]+j) \\2.\ if(t[i-1]+k+m<=t[i]+j)\ f[i][j]=min(f[i][j],f[i-1][k]+j) \]
那麼三重迴圈就解決了呀。
\(Code:\)
#include<bits/stdc++.h>
using namespace std;
const int N=580,M=580,T=4*1e6+80,INF=0x3f3f3f3f;
int n,m,t[N]={},f[N][2*M]={},ans=INF;
inline void input(void)
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)scanf("%d",&t[i]);
}
inline void init(void)
{
sort(t+1,t+n+1);
for(int i=1;i<=m*2;i++)f[1][i]=i;
}
inline void solve(void)
{
for(int i=2;i<=n;i++)
{
for(int j=0;j<2*m;j++)
{
f[i][j]=INF;
for(int k=0;k<2*m;k++)
{
if(t[i-1]+k==t[i]+j||t[i-1]+k+m<=t[i]+j)f[i][j]=min(f[i][j],f[i-1][k]+j);
}
if(i==n)ans=min(ans,f[i][j]);
}
}
}
int main(void)
{
freopen("bus.in","r",stdin);
freopen("bus.out","w",stdout);
input();
init();
solve();
printf("%d\n",ans);
return 0;
}
<後記>後記>