【高中组集体赛前热身赛】题解
(第一次给学弟们学妹写题解,可能有些地方我认为简单的可能没有仔细解释,所以导致没有讲清楚的一定要勤奋问百度,或者底下留言)
题目选取上,都是选取的代码量短的题目,但是后面的题目代码量可能会稍增加。
练习可以理解代码能力,旨在可以看题解代码,也能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; }
It is your time to fight!