1. 程式人生 > >【NOIP2016 普及組題解分析總結】

【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]]);
}


      奮鬥,拼搏!