NOIP2017賽前模擬11月2日總結
分數爆炸的一天··但也學了很多
題目1:活動安排
給定n個活動的開始時間與結束時間··只有一個場地··要求保留盡量多的活動且時間不沖突···場地數n<=100000
考點:貪心
直接將結束時間按照升序排序,然後從小到大取不沖突的即可··很像hdu4343,然而我做的時候有點搞麻煩了
#include<iostream> #include<cstdio> #include<cstdlib> #include<cmath> #include<ctime> #include<cctype> #include<string> #include<cstring> #include<algorithm> using namespace std; const int N=1e5+5; struct node{int l,r;}q[N],a[N]; int n,stk[N],top=0; 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; } inline bool cmp(node a,node b){ if(a.l==b.l) return a.r>b.r; else return a.l<b.l; } inline void pre(){ sort(q+1,q+n+1,cmp); for(int i=1;i<=n;i++){while(top&&q[i].l>=q[stk[top]].l&&q[i].r<=q[stk[top]].r) top--; stk[++top]=i; } n=0; for(int i=1;i<=top;i++) a[++n]=q[stk[i]]; } int main(){ //freopen("arrange.in","r",stdin); //freopen("arrange.out","w",stdout); n=R(); for(int i=1;i<=n;i++){ int h=R(),m=R();q[i].l=h*60+m; h=R();m=R(); q[i].r=h*60+m; } pre(); int head=1,tail,ans=0; while(head<=n){ if(head<=n) ans++; else break; tail=head+1; while(tail<=n){ if(a[tail].l>=a[head].r) break; else tail++; } if(tail>n) break; else head=tail; } cout<<ans<<"\n"; }
題目2:最佳序列
給定n個非負整數序列,要求找出長度大於l,小於r的區間,使得區間的平均數最大,n<=20000
考點:二分+單調隊列
首先看到平均數要想到老套路:將每個值減去平均數轉化成區間和大於0的問題
求最大的平均數自然想到二分··然後用單調隊列判斷是否有可行解即可
#include<cstdio> #include<cstdlib> #include<cmath> #include<ctime> #include<cctype> #include<string> #include<cstring> #include<algorithm> #include<iostream> #include<queue> using namespace std; const int N=2e4+5; const double eps=1e-7; 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 n,l,r,num[N],minn,maxx; double ans=0,sum[N]; inline bool jud(double k){ for(int i=1;i<=n;i++) sum[i]=(double)num[i]-k,sum[i]+=sum[i-1]; deque<int>que; for(int i=l;i<=n;i++){ while(!que.empty()&&sum[que.back()]>=sum[i-l]) que.pop_back(); que.push_back(i-l); if(sum[i]>=sum[que.front()]) return true; while(!que.empty()&&que.front()<=i-r) que.pop_front(); } return false; } int main(){ //freopen("a.in","r",stdin); //freopen("a.out","w",stdout); n=R(),l=R(),r=R();minn=1000005,maxx=0; for(int i=1;i<=n;i++) num[i]=R(),minn=min(minn,num[i]),maxx=max(maxx,num[i]); double le=minn,ri=maxx; while(ri-le>eps){ double mid=(le+ri)/2; if(jud(mid)) ans=max(ans,mid),le=mid; else ri=mid; } printf("%0.4f\n",ans); return 0; }
題目3:回文子串
給定兩個串s和t··將s與t拆開後組成新的字符串,但新串的字母的順序為原來s與t的順序··求新串能夠包含的最長回文子串長度,s與t的長度均小於50
考點:dp
哎··dp還是太弱了··開始時完全沒思路
用f[i][j][k][l]表示s串從前往後已經取出了i個,從後往前已經取出了k了,t串從前往後已經取出了j個,從後往前已經取出了l個組成的字符串的最長回文子串長度
轉移看代碼吧··懶得寫了
#include<iostream> #include<cstdio> #include<cstdlib> #include<cmath> #include<ctime> #include<cctype> #include<string> #include<cstring> #include<algorithm> using namespace std; const int N=55; char s[N],t[N]; int ans,f[N][N][N][N],n,m; int main() { //freopen("a.in","r",stdin); scanf("%s%s",s+1,t+1); n=strlen(s+1);m=strlen(t+1); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) for(int k=n;k>=i-1;k--) for(int l=m;l>=j-1;l--){ if(i<k&&s[i]==s[k]) f[i+1][j][k-1][l]=max(f[i+1][j][k-1][l],f[i][j][k][l]+2); if(j<l&&t[j]==t[l]) f[i][j+1][k][l-1]=max(f[i][j+1][k][l-1],f[i][j][k][l]+2); if(i<=k&&j<=l){ if(s[i]==t[l]) f[i+1][j][k][l-1]=max(f[i+1][j][k][l-1],f[i][j][k][l]+2); if(s[k]==t[j]) f[i][j+1][k-1][l]=max(f[i][j+1][k-1][l],f[i][j][k][l]+2); } } for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) for(int k=i;k>=i-1;k--) for(int l=j;l>=j-1;l--){ if(i==k&&j>l) ans=max(ans,f[i][j][k][l]+1); if(i>k&&j==l) ans=max(ans,f[i][j][k][l]+1); if(i>k&&j>l) ans=max(ans,f[i][j][k][l]); } cout<<ans<<endl; return 0; }
題目4:賭博遊戲
最近西雅圖的高中校園裏流行這樣一個遊戲。
我們有一個骰子,這個骰子有M個面,分別寫著1..M,並且是個公平的骰子,換句話說,一次投擲時每個面朝上的概率是相同的。
遊戲的組織者使用這個骰子進行N次投擲,並且告訴玩家點數v出現了至少一次。那麽玩家需要猜測N次投擲的點數之和。如果猜對了,就贏得了這個遊戲。
小宇也喜歡玩這個遊戲。在一次遊戲中,她猜測了一個正整數sum,於是她想知道猜對的概率是多少。
數據滿足:1≤N,M≤50;1≤v≤M;1≤sum≤N*M。
考點:概率dp
簡單的概率dp題··用f[j][i][0/1]表示已經擲出了i次骰子,總和為j,是否擲出了v的概率
dp方程看代碼
#include<iostream> #include<cstdio> #include<cstdlib> #include<cmath> #include<ctime> #include<cctype> #include<cstring> #include<string> #include<algorithm> using namespace std; double f[2505][55][2]; int n,m,v,sum; int main(){ //freopen("a.in","r",stdin); scanf("%d%d%d%d",&n,&m,&v,&sum); for(int i=1;i<=m;i++){ if(i==v) f[i][1][1]=1.0/m; else f[i][1][0]=1.0/m; } for(int i=1;i<n;i++) for(int j=1;j<=i*m;j++) for(int k=1;k<=m;k++){ if(k!=v) f[j+k][i+1][0]+=f[j][i][0]*1.0/m,f[j+k][i+1][1]+=f[j][i][1]*1.0/m; else f[j+v][i+1][1]+=(f[j][i][0]*1.0/m+f[j][i][1]*1.0/m); } double tot=0; for(int i=1;i<=n*m;i++) tot+=f[i][n][1]; printf("%0.8f",f[sum][n][1]/tot); return 0; }
NOIP2017賽前模擬11月2日總結