紀中訓練 day5 【NOIP普及組】模擬賽D組 解題報告
阿新 • • 發佈:2019-02-01
目錄
大意
從左到右,問加了一個’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);//輸出
}