Live2d Test Env

【高中组集体赛前热身赛】题解

(第一次给学弟们学妹写题解,可能有些地方我认为简单的可能没有仔细解释,所以导致没有讲清楚的一定要勤奋问百度,或者底下留言)

 题目选取上,都是选取的代码量短的题目,但是后面的题目代码量可能会稍增加。

练习可以理解代码能力,旨在可以看题解代码,也能Understand。

 

                             ------分界线-------

 

 

 

 

【A-投掷硬币】 

题解:基础DP(动态规划),dp[i][j]表示从第一个硬币开始翻,翻到第i个硬币的时候,翻到正面的有j个。那么dp[i][j]由dp[i-1][j-1](第i个是正面)和dp[i-1][j](第i个是反面)转移而来。所以由方程:dp[i][j]=dp[i-1][j]*(1.0-p[i])+dp[i-1][j-1]*p[i];当然j=0的话只能优dp[i-1][0]转移而来。

 

就是把硬币排成一排,从左向右一个一个翻,dp[i][j]表示翻完前面i个后,有j个正面的概率。
那么对于第i个,dp[i][j]=前面的有j个正面的概率*第i个反面的概率+前面j-1个正面的概率*第i个正面的概率。

 

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
using namespace std;
const int maxn=1010;
double dp[maxn][maxn],p[maxn];
int main()
{
    int N,M,i,j;
    scanf("%d%d",&N,&M);
    dp[0][0]=1.0;
    for(i=1;i<=N;i++) scanf("%lf",&p[i]);
    for(i=1;i<=N;i++){
        dp[i][0]=dp[i-1][0]*(1.0-p[i]);
        for(j=1;j<=M;j++){
           dp[i][j]=dp[i-1][j]*(1.0-p[i])+dp[i-1][j-1]*p[i];
        }
    }
    printf("%.4lf\n",dp[N][M]);
    return 0;
}

【B-最大集合】

题解:每一个集合是封闭的。即每个元素只会出现在一个集合里面,而且从每个集合的任意元素出发,都可以走到下一个元素,直到走到起始元素。

所以DFS搜索就可以了,把每次搜索到的标记一下。对于没有搜索到的,作为起点,继续搜索。

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=100010;
int vis[maxn],a[maxn],cnt;
int main()
{
    int N,i,ans=0,tmp;
    scanf("%d",&N);
    for(i=1;i<=N;i++) scanf("%d",&a[i]);
    for(i=1;i<=N;i++){
        if(vis[i]==0){
            cnt=1; tmp=i;
            vis[tmp]=1;
            while(!vis[a[tmp]]){
                cnt++;
                tmp=a[tmp];
            }
            ans=max(ans,cnt);
        }
    }
    printf("%d\n",ans);
    return 0;
} 

【C-第K小分数】

枚举每一个数为分母,然后二分分子。 二分的复杂度是O(logn),然后枚举N个数作为分母,这样的话复杂度就是O(N*logN)。不会二分的仔细看代码就会了。

#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
#define ll long long
const int maxn=1010;
ll a[maxn],N;
ll check(int x,int y)
{
    ll res=0;
    for(int i=1;i<=N;i++){
        res+=(a[i]*x/y);
    } return res;
}
int main()
{
    ll K,L,R,Mid,tmp;
    scanf("%lld%lld",&N,&K);
    for(int i=1;i<=N;i++) scanf("%d",&a[i]);
    for(int i=1;i<=N;i++){
        L=1; R=a[i]-1;
        while(L<=R){
            Mid=(L+R)/2;
            tmp=check(Mid,a[i]);    
            if(tmp==K){
                printf("%lld/%lld\n",Mid,a[i]);
                return 0;
            }
            else if(tmp<K) L=Mid+1;
            else R=Mid-1;
        }
    }return 0;
}

【D-最大子矩阵】

由于A>=1满足区间和的单调性。所以可以用双指针,即枚举矩形的上下边界,然后移动左右边界

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=300;
int N,M,K,a[maxn][maxn],sum[maxn][maxn];
int vis[10010],num,Now,ans=-1;
int main()
{
    int i,j,k,L,R;
    scanf("%d%d%d",&N,&M,&K);
    for(i=1;i<=N;i++)
      for(j=1;j<=M;j++){
        scanf("%d",&a[i][j]);
        sum[i][j]=sum[i-1][j]+a[i][j];
    }
    
    for(i=1;i<=N;i++)
      for(j=i;j<=N;j++){ 
         memset(vis,0,sizeof(vis));
         num=0; Now=0;
         for(R=1,L=1;R<=M;R++){
             num+=j-i+1; Now+=sum[j][R]-sum[i-1][R];
             while(Now>K&&L<=R) {
                num-=j-i+1;
                Now-=sum[j][L]-sum[i-1][L];
                L++;
             }
             if(Now<=K&&L<=R) ans=max(ans,num);
         }
    }
    printf("%d\n",ans);
    return 0;
}

 

posted @ 2018-02-21 20:55  nimphy  阅读(183)  评论(0编辑  收藏  举报