NOIP2017賽前模擬11月2日總結

分數爆炸的一天··但也學了不少node

題目1:活動安排

  給定n個活動的開始時間與結束時間··只有一個場地··要求保留儘可能多的活動且時間不衝突···場地數n<=100000ios

  考點:貪心post

  直接將結束時間按照升序排序,而後從小到大取不衝突的便可··很像hdu4343,然而我作的時候有點搞麻煩了spa

  

#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<=20000code

  考點:二分+單調隊列blog

  首先看到平均數要想到老套路:將每一個值減去平均數轉化成區間和大於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;
}
相關文章
相關標籤/搜索