NOIP2017賽前模擬1:總結
題目:
1.造盒子
題目描述
企鵝豆豆收到了面積為 K 的一塊橡皮泥。但是他沒有合適的盒子來裝下這個橡皮泥。所以他打算造一個盒子。
制造臺是有方形網格的平臺,每個小正方形邊長為 1 。現在豆豆有兩類木板,一類只能放在小正方形的邊上,一類只能放在小正方形的對角線上。
現在豆豆想知道最少需要用多少塊木板來制造一個封閉的盒子來把橡皮泥放下去。
輸入格式
第一行一個整數 T 表示數據組數。
對於每組數據的第一行一個整數 K ,表示橡皮泥的大小。
輸出格式
輸出一個整數表示最少需要的木板數目
樣例數據 1
輸入 [復制]
5
1
2
3
4
5
輸出
4
4
6
6
7
備註
【數據範圍】
對於 50% 的數據,K≤20。
對於 100% 的數據,K≤109。
2.分玩具
題目描述
豆豆和豆沙正在分一些玩具,每個玩具有一個好玩值,每個人可以拿走任意數量的玩具,獲得的愉快度為最小的好玩值。現在豆豆先拿,每個人輪流操作,直到沒有玩具可以拿。豆豆想知道他能比豆沙多出多少愉快度?
輸入格式
第一行 N 表示玩具個數。
接下來一行 N 個整數表示第 i 個玩具的好玩值。
輸出格式
輸出一個整數表示最多多出的愉快度。
樣例數據 1
輸入 [復制]
3
1 3 1
輸出
2
備註
【數據範圍】
對於 30% 的數據,N≤10。
對於 70% 的數據,N≤1000。
對於 100 %的數據,N≤1000000,0≤數值範圍≤109。
3.problem
題目描述
今天豆豆在做作業的時候遇到這麽一個問題:
給出 N 個正整數 a1..aN ,再給出一個正整數 k ,現在可以進行如下操作:每次選擇一個大於 k 的正整數 ai,將 ai 減去 1 ,選擇 a[i-1] 或 a[i+1] 中的一個加上 1 。經過一定次數的操作後,最大能夠選出多長的一個連續子序列,使得這個子序列的每個數都不小於 k 。
總共有 M 次詢問,每次詢問給出的一個 k ,回答這個詢問。
輸入格式
第一行兩個整數 N , m 代表數組長度和詢問個數。
接下來一行,第 i 個正整數表示 ai 。
第三行 M 個正整數,第 i 個正整數表示第 i 次詢問的 k 。
輸出格式
對於每個詢問,輸出一個整數表示問題的答案。
樣例數據 1
輸入 [復制]
5 6
1 2 1 1 5
1 2 3 4 5 6
輸出
5 5 2 1 1 0
備註
對於 20% 的數據,N≤10。
對於 40% 的數據,N≤1000。
對於 50% 的數據,N≤100000。
對於 100% 的數據,N≤1000000,M≤20,ai≤109,k≤109。
請選手註意自己的常數。
題解:
1.找規律
首先找到最‘劃算’的填補方式·····當面積等於k*k*2時··無疑是斜著的正方形最優··然後沿著四條邊一次填補·····於是規律就可以找到了··
代碼:
#include<iostream> #include<cstdio> #include<cstdlib> #include<cmath> #include<ctime> #include<cctype> #include<cstring> #include<string> #include<algorithm> using namespace std; int a,t; int ans[21]={0,4,4,6,6,7,8,8,8,9,10,10,10,11,11,12,12,12,12,13,13}; inline int R() { char c;int f=0; for(c=getchar();c<‘0‘||c>‘9‘;c=getchar()); for(;c<=‘9‘&&c>=‘0‘;c=getchar()) f=(f<<3)+(f<<1)+c-‘0‘; return f; } int main() { t=R(); while(t--) { a=R(); if(a<=20) cout<<ans[a]<<endl; else { int k=(int)sqrt(a/2); if(k*k*2==a) cout<<k*4<<endl; else if(a>k*k*2&&a<=(2*k*k+(k-1))) cout<<k*4+1<<endl; else if(a>(2*k*k+k-1)&&a<=(2*k*k+2*k)) cout<<k*4+2<<endl; else if(a>(2*k*k+2*k)&&a<=(2*k*k+3*k)) cout<<k*4+3<<endl; else if(a>(2*k*k+3*k)&&a<=(2*k*k+4*k+1)) cout<<k*4+4<<endl; } } return 0; }
2.dp
先將玩具從大到小排序然後離散化·····用dp[i]表示先手選擇i-n範圍內的玩具時最多多出的好玩度·····得出dp方程:
dp[i]=max(maxx,num[i]-dp[i-1])
其中maxx是max(dp[i-1——n])(因為兩個人都很聰明··肯定會選最大值····)
md這個dp方程太巧妙了···
代碼:
#include<iostream> #include<cstdio> #include<cstdlib> #include<cmath> #include<ctime> #include<cctype> #include<cstring> #include<string> #include<algorithm> using namespace std; const int N=1e6+5; int ans,maxx,n,dp[N],num[N]; inline int R() { char c;int f=0; for(c=getchar();c<‘0‘||c>‘9‘;c=getchar()); for(;c<=‘9‘&&c>=‘0‘;c=getchar()) f=(f<<3)+(f<<1)+c-‘0‘; return f; } bool cmp(int a,int b) { return a>b; } int main() { n=R(); for(int i=1;i<=n;i++) num[i]=R(); sort(num+1,num+n+1,cmp); n=unique(num+1,num+n+1)-num-1; dp[n]=num[n]; for(int i=n;i>=1;i--) dp[i]=maxx=max(maxx,num[i]-dp[i+1]); cout<<dp[1]<<endl; return 0; }
3.單調棧
這道題就是找最長的一段平均值大於k的長度·····
為了消除k的影響,我們將每個數減去k··這樣就是找最長的一段平均值大於0的長度····
然後計算前綴和···我們對於sum[i],我們要找到sum[1-i-1]中小於sum[i]且最靠近左邊的j用i-j更新答案···
因此我們一邊從1到n枚舉i,一邊維護一個sum遞減的單調隊棧···
然後記錄一個指針tail,如果sum[i]>sum[tail],就一直將指針向左移動找到邊界更新答案··反之就往左移···
這樣由於每次往右移動最多一次(保證答案最優)···而往左移動的次數是小於等於往右移動的次數的···所以每次詢問的復雜度為O(n)·······
代碼:
#include<iostream> #include<cstdio> #include<cstdlib> #include<cmath> #include<ctime> #include<cctype> #include<cstring> #include<string> #include<algorithm> using namespace std; const int N=1e6+5; long long num[N],sum[N]; int n,m,k,tot,tail,ans,stack[N]; inline int R() { char c;int f=0; for(c=getchar();c<‘0‘||c>‘9‘;c=getchar()); for(;c<=‘9‘&&c>=‘0‘;c=getchar()) f=(f<<3)+(f<<1)+c-‘0‘; return f; } int main() { //freopen("a.in","r",stdin); n=R();m=R(); for(int i=1;i<=n;i++) num[i]=R(),num[i]+=num[i-1]; while(m--) { k=R();ans=0;tot=0; for(int i=1;i<=n;i++) sum[i]=num[i]-(long long)k*i; for(int i=1;i<=n;i++) { if(sum[i]>=0) ans=max(ans,i); if(!tot) stack[++tot]=i,tail=tot; else if(sum[i]<sum[stack[tot]]) { stack[++tot]=i,tail=tot; if(sum[i]-sum[i-1]>=0) ans=max(ans,1); } else { if(sum[stack[tail]]>sum[i]) { if(tail+1<=tot) tail++; if(sum[stack[tail]]<=sum[i]) ans=max(ans,i-stack[tail]); } else { ans=max(ans,i-stack[tail]); while(sum[stack[tail-1]]<=sum[i]&&tail-1>0) tail--; ans=max(ans,i-stack[tail]); } } } cout<<ans<<" "; } return 0; }
NOIP2017賽前模擬1:總結