【NOIP2016 普及組題解分析總結】
莫名的呵呵,普及組考完了,就這麼呵呵地考完了。。。心態放平靜,肯定有人考的比你好,剛剛到洛谷去測了測點選開啟連結(民間資料)慘淡的270 。。。。。啊啊啊啊啊shit來 開(xin) 心(hui) 快(yi) 樂(leng) 地分析一下吧
1.pencil
這道。。史無前例的water啊,小學生都能AC吧。。只不過判斷一下整除啊,注意它是隻買一種包裝,3個價格乘以件數後取最小就可以了,記得賦初值什麼的不知道為什麼最近特別喜歡打句號啊。。。
#include<cstdio> int min(int a,int b){return a<b?a:b;} int main() { int n,a,b,s=100000001; scanf("%d",&n); for(int i=1;i<=3;i++) { scanf("%d%d",&a,&b); if(n%a==0) b=n/a*b; else b=(n/a+1)*b; s=min(s,b); } printf("%d",s); fclose(stdin); fclose(stdout); return 0; }
2.date
。。。坑特別多啊,不得不說這道題真的很猥瑣啊,不過還好我AC了~
我的策略就是把處在輸入中間的那些年份列舉,一年一年加,然後判斷由前四位數生成的這個迴文日期是否合法,不過要特殊判斷第一年和最後一年是否在給定範圍內,注意閏年~
#include<cstdio> const int t[14]={-4,31,28,31,30,31,30,31,31,30,31,30,31}; char n[15],m[15]; int a,b,s,c,q,w; int xmy(int x) { int c=0,r=x; for(int i=1;i<=4;x/=10,i++) c=c*10+x%10; if(c/100>12) return 0; int o=t[c/100]; if(((r%4==0&&r%100!=0)||(r%400==0))&&c/100==2) o++; if(c%100>o) return 0; return 1; } bool xmy1(int x) { c=0; int r=x; for(int i=1;i<=4;x/=10,i++) c=c*10+x%10; if(c/100>12) return 0; int o=t[c/100]; if(((r%4==0&&r%100!=0)||(r%400==0))&&c/100==2) o++; if(c%100>o) return 0; return 1; } int main() { scanf("%s%s",n,m); for(int i=0;i<4;i++) a=a*10+n[i]-'0',b=b*10+m[i]-'0'; for(int i=4;i<8;i++) q=q*10+n[i]-'0',w=w*10+m[i]-'0'; for(int i=1;a+i<b;i++) s+=xmy(a+i); if(a==b) { if(xmy1(a)&&q<=c&&c<=w) s++; } else { if(xmy1(a)&&q<=c) s++; if(xmy1(b)&&c<=w) s++; } printf("%d",s); return 0; }
3.port
我表示呵呵,這空間。。這時間。。嗯。。好,果斷選擇了70%,想了想,用陣列的話肯定要炸,開100000的話直接0分,所以。。還是用佇列吧,把每一艘船都push進去,如果和隊首相差一天就把隊首pop掉,再把vis[那艘船上的人的國籍]--;
int vis[100005];
struct node
{
int t,k,r[100005];
}h;
queue<node>a;
定義差不多就這樣。。其實70分也挺水的。主要是後面超時。。可能也會爆空間
來繼續分析正解吧,仔細審題,查詢蹊蹺之處,為什麼題目只給出了k的總和的範圍而沒有說單個的k的範圍?
是否有種演算法只與K有關?so,看一看,k是指人的總數,那麼,我們用佇列存人怎麼樣?不用存船,把每個人的國籍和時間存進去就好了啊,而且時間複雜度不高,思路其實差不多,空間也最多600000。
實現比較簡單,就是push(每個人)就可以了,上程式碼吧。
#include<cstdio>
#include<queue>
using namespace std;
int n,x,y,s,o;
int vis[100005];
struct node
{
int t,r;
}h,u;
queue<node>a;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d%d",&o,&y);
for(int j=1;j<=y;j++)
{
scanf("%d",&h.r);
h.t=o;
a.push(h);
if(!vis[h.r]) s++;
vis[h.r]++;
}
while(1)
{
u=a.front();
if(h.t-86400>=u.t)
{
vis[u.r]--;
if(!vis[u.r]) s--;
a.pop();
}
else break;
}
printf("%d\n",s);
}
fclose(stdin);
fclose(stdout);
return 0;
}
4.magic
我跪了。。。啊啊啊啊啊0分。。。看到分數我就懵逼了,我知道遞迴肯定超時,但不至於一分不得吧!後來。。我發現。。我就sort了一下就傻逼的忘了判斷大小。神吶!!!讓我加一個if吧!!!
唉~不要抱怨,總結一下吧,還是too young too simple,以為sort一下就不用管那個大於符號了,嚴格不等號啊,等於的情況沒判斷啊。。45分沒了。。還是細節啊,題目一定要讀透,把每個條件羅列在紙上,確保每一步都毫無遺漏的完成,才不留遺憾啊。做題,一定要穩!
現在來找找AC方式,首先,剖析題目,列出條件:Xa<Xb<Xc<Xd,Xb-Xa=2(Xd-Xc),Xb-Xa<Xc-Xb/3
遇到這種有條件的題,通常把圖形畫出來比較直觀。
如圖所示,若把d點確定,設c-d距離為i,則a,b的距離就是2i,則b,c的距離>2*(a-b)也就是>6i,總距離大於9i,那麼我們的外層迴圈就列舉i,再列舉d的位置,d的方案數就等於(前面所有a的方案)*(前面所有b的方案)*(當前c的方案數),c的方案數=(前面所有a的方案)*(前面所有b的方案)*(當前d的方案數),同理,列舉a的位置,也可以得到a與b的方案數。
當然,說了這麼多估計你也可能是懵逼的,看看程式碼再結合一下分析吧。
先看看pascal
var
a,b,c,d,w:array[0..15005]of longint;//a,b,c,d,a[i]表示以魔法值i作為魔法陣中a元素的總方案數
i,j,n,m,x,y:longint;//定義變數
begin
readln(n,m);//讀入n,m
for i:=1 to m do//begin,end相當於c++的{}
begin
read(h[i]);
inc(w[h[i]]);//w[h[i]]++;表示這個值的數增加了一個
end;
for i:=1 to n div 9 do//n div 9 == n/9(除數為整數)
begin
x:=1+9*i; y:=0;
for j:=2+9*i to n do//j->d
begin
y:=y+w[(j-x)]*w[j-x+i+i];//w[j-x]:魔法陣為a元素的個數,w[j-x+2*i]:b元素的個數,
//因為是至少為9*i+1,所以在列舉後面的d時,要將前面的所有a,b的情況累加起來
d[j]:=d[j]+y*w[j-i];//a*b*c==d
c[j-i]:=c[j-i]+y*w[j];//a*b*d==c,因為c與d的距離固定為i,所以確定d就可以確定c
end;
x:=8*i+1; y:=0;
for j:=n-9*i-1 downto 1 do//j->a
begin
y:=y+w[j+x]*w[j+x+i];//w[j+x]:魔法陣為c元素的個數,w[j+x+i]:d元素的個數
a[j]:=a[j]+y*w[j+i+i];//同理
b[j+i+i]:=b[j+i+i]+y*w[j];
end;
end;//上下兩迴圈可以調換順序,因為a,b,c,d只與i和w有關,和a[],b[],c[],d[]無關
for i:=1 to m do writeln(a[h[i]],' ',b[h[i]],' ',c[h[i]],' ',d[h[i]]);//a[h[i]]:以物品一的魔法值作為法陣a元素的總方案數
close(input);close(output);
end.
pascal語言可能看不懂,看看我寫的C++吧(分析自己看上面)
#include<cstdio>
#define M 15005
int a[M],b[M],c[M],d[M],w[3*M],h[3*M];
int n,m,i,j,x,s;//s就是上面的y,累加和
int main()
{
scanf("%d%d",&n,&m);
for(i=1;i<=m;i++)
{
scanf("%d",&h[i]);
w[h[i]]++;
}
for(i=1;9*i<n;i++)//注意是總長度>9*i,邊界一定要考慮無誤,想通為什麼
{
x=9*i+1;s=0;//設邊界最好畫個草圖自己算一算
for(j=9*i+2;j<=n;j++)
{
s+=w[j-x]*w[j-x+2*i];
d[j]+=s*w[j-i];
c[j-i]+=s*w[j];
}
s=0;
for(j=n-x;j>=1;j--)//注意迴圈不能順序,因為s的累加和會改變,a[j]會加上後面的c,d,而不是前面的
{
s+=w[j+x]*w[j+x-i];
a[j]+=s*w[j+2*i];
b[j+2*i]+=s*w[j];
}
}
for(i=1;i<=m;i++)
printf("%d %d %d %d\n",a[h[i]],b[h[i]],c[h[i]],d[h[i]]);
}
奮鬥,拼搏!