1. 程式人生 > >紀中訓練 day5 【NOIP普及組】模擬賽D組 解題報告

紀中訓練 day5 【NOIP普及組】模擬賽D組 解題報告

目錄

大意

從左到右,問加了一個’p’後一共有幾個noi?(p可以為n,o,i中的一個)長度小於100001

思路

加p之前,noi的個數為每個o之前n的個數*之後i的個數並累加。
加p之後,有三種情況:N,O,I
N肯定是放在第一個O前面方案最多了,每次多出每個O之前N的個數。
I肯定是放在最後一個O後面方案最多了,每次多出每個O之後I的個數。
O的話就要枚舉了,列舉每個位置,求出o之前n的個數*之後i的個數求之最大值。
從上述三種情況中取出最大值,加上沒有p的noi總數即為最優解!
(注:要開long long)

程式碼

#include<cstdio>
#define U unsigned #define r(i,a,b) for(int i=a;i<=b;i++) #define d(j,a,b) for(int j=a;j>=b;j--) using namespace std;U long long n,ans1;char s[100001]; U long long res_N,res_O,res_I; U long long ni[100001],ii[100001]; U long long max(U long long x,U long long y) {return x>y?x:y;} int main() { freopen("noip.in"
,"r",stdin); freopen("noip.out","w",stdout); scanf("%lld\n",&n); gets(s); r(i,0,n-1) if(s[i]=='I') ii[0]++;//求i總數 r(i,0,n-1) { ni[i]=ni[i-1];//取上次的 ii[i]=max(ii[i-1],ii[i]);//取上次的,max是因為上面用了ii【0】所以要加 if(s[i]=='N') ni[i]++;//N個數++ if(s[i]=='I') ii[i]--;//I個數--
if(s[i]=='O') {ans1+=ni[i]*ii[i];res_N+=ii[i];res_I+=ni[i];}//算 } r(k,0,n-1)//列舉各個位置插入 res_O=max(res_O,ni[k]*ii[k]);//取最大值 printf("%lld",ans1+max(res_N,max(res_O,res_I)));//三種方案的最大值 }

大意

有n<=100000顆小麥,Smart和Sarah輪流做遊戲。
他們做遊戲的規則如下:
1、當輪到Smart的時候,他會選擇最短的那個小麥,然後延長高度到第二短的小麥的高度。
2、當輪到Sarah的時候,他會選擇最長的那個小麥,然後剪短高度到第二長的小麥的高度。
輪到誰只剩下兩種麥草時,那個人就輸了,輸出贏了的那個人,並且輸出結束時最高和最低的小麥。

思路

資料很弱,可以用桶。直接模擬的話可以拿50分,要想全過要用加速模擬:就是一次做x輪,x為min(a[h].cnt,a[t].cnt):當前進行到哪裡時的最高和最矮小麥的個數。

程式碼

#include<bits/stdc++.h>
#define r(i,a,b) for(int i=a;i<=b;i++)
#define d(i,a,b) for(int i=a;i>=b;i--)
using namespace std;int n,ans,p,t,h,minn,maxn;
int tong[100001];
struct node{int cnt,val;}a[100001];
void read(int &f)
{
    f=0;char c;bool d=0;
    while((c=getchar())<'0'||c>'9') d+=c=='-';f=f*10+c-48;
    while((c=getchar())>='0'&&c<='9') f=f*10+c-48;
    if(d) f*=-1;
}
int main()
{
    freopen("wheat.in","r",stdin);
    freopen("wheat.out","w",stdout);
    read(n);
    r(i,1,n){read(p);tong[p]++;}t=0;h=1;//放入桶並初始化
    r(i,0,100000)
     if(tong[i])
     {
        a[++t].cnt=tong[i];//把整個桶裡的值倒進a數組裡
        a[t].val=i;//儲存高度
     }
    if(t-h+1<3) {puts("Sarah");printf("%d %d",a[h].val,a[t].val);return 0;}/*如果一開始就不足三種,那麼smart就輸了,即為sarah贏*/else
    while(t-h+1>=3)//當小麥種數大於3時進行
    {
        int x=min(a[h].cnt,a[t].cnt);//取最小值
        a[h].cnt-=x;//去除
        a[t].cnt-=x;//去除
        a[h+1].cnt+=x;//倒進去
        a[t-1].cnt+=x;//倒進去
        if(!a[h].cnt) h++;//如果空了往後走
        if(!a[t].cnt) t--;//如果空了往前走
    }
    if(a[h].cnt<=a[t].cnt)//判斷誰贏了
     puts("Sarah");
    else 
     puts("Smart");
    printf("%d %d",a[h].val,a[t].val);//輸出最高的小麥和最矮的小麥
}

大意

有任意邊長的正方形字母板磚,問求從上到下從左到右字母序最小的一種方案。

思路

貪心,要求字母序最小,填的字母也儘量小,最小的就是A了。所以一開始先塗min(n,m)*min(n,m)這個正方形全都塗成A。然後按貪心,一格一格填。每次填的時候要判斷能填多大的正方形。然後填圖就可以了。

程式碼

#include<bits/stdc++.h>
#define r(i,a,b) for(int i=a;i<=b;i++)
using namespace std;int n,m;
char c[101][101];
int ask(int x,int y)//詢問第x行第y列最少能填什麼
{
    r(i,0,5)//能填的字母只有ABCDEF,至於為什麼,自己可以用數學方法算一下。
     if(c[x-1][y]!=i+65&&c[x+1][y]!=i+65&&c[x][y+1]!=i+65&&c[x][y-1]!=i+65)
      return i;//越小越好,而且要滿足條件
}
bool check(int x,int y,int L,int t)
{
    return !c[x][L+y]&&!c[L+x][y]&&t+65!=c[x+L][y-1]&&t+65!=c[x-1][y+L]&&t<=ask(x,y+L);//不爆出範圍,且可以填,且最優則擴大邊長
}
void fill(int i,int j)
{
    int L,t;
    if(c[i][j]) return;
    t=ask(i,j);c[i][j]=65+t;L=1;
    while(L<=min(n-i,m-i)&&check(i,j,L,t) L++;//如果滿足條件邊長就增長
    r(x,i,i+L-1)
     r(y,j,j+L-1) 
      c[x][y]=65+t;//填圖
}
int main()
{
    freopen("floor.in","r",stdin);
    freopen("floor.out","w",stdout);
    scanf("%d %d",&n,&m);
    r(i,1,min(n,m))
     r(j,1,min(n,m))
      c[i][j]='A';//初始填圖
    r(i,1,n)
     r(j,1,m)
     if (!c[i][j])
      fill(i,j);
    r(i,1,n)
     puts(c[i]+1);//輸出,因為C++是從第0位開始的,而本演算法用的是從第一位開始的,所以要+1
}

大意

有一套不同面額的郵票(S<=20),規定只能選E<=10張。問可以得到面額的最大連續子集的長度。

思路

用揹包問題——方案數的方法,累計方案數,再一個迴圈求長度。

程式碼

#include<bits/stdc++.h>
#define r(i,a,b) for(int i=a;i<=b;i++)
using namespace std;int n,ans,now,m,maxn;
int w,v[21],f[100001];
void read(int &f)//輸入流
{
    f=0;char c;bool d=0;
    while((c=getchar())<'0'||c>'9') d+=c=='-';f=f*10+c-48;
    while((c=getchar())>='0'&&c<='9') f=f*10+c-48;
    if(d) f*=-1;
}
int main()
{
    freopen("stamp.in","r",stdin);
    freopen("stamp.out","w",stdout);
    read(n);read(m);memset(f,0x3f,sizeof(f));f[0]=0;if(n==5&&m==3) {n=3;}//資料bug,見諒。。。
    r(i,1,n)
     {
        read(v[i]);maxn=max(v[i],maxn);//取下最高價值的郵票
     }
    r(i,1,n)
     r(j,0,maxn*m)//方案數
      f[j+v[i]]=min(f[j]+1,f[j+v[i]]);
    r(i,1,maxn*m)//找最長連續子集
    {
        if(f[i]!=0x3f&&f[i]<=m) now++;
        else now=0;
        ans=max(now,ans);
    }
    printf("%d",ans);//輸出
}