Codeforces Round #521 (Div. 3)
B 题过的有些牵强,浪费了很多时间,这种题一定想好思路和边界条件再打,争取一发过。
D 题最开始读错题,后面最后发现可以重复感觉就没法做了,现在想来,数据量大,但是数据范围小
枚举不行,二分还是可以的,还是自己的二分水平太差了,平时周一到周四做CF,周五周末复习CF,学习
新的算法,并抽空进行专项训练
D题的思路其实很简单,我通过二分一个copy的次数val,这样我再去用每个数出现的CNT[i]/val,就是这个数
回在T序列中出现多少次,那么check直接上就行,但是要注意,我需要输出的不是val,而是序列T,那么我需要
每次跑一下,看是否满足,但是到了边界条件时,发现已经不满足了,但是这时ans数组已经改变,不过不要担心,我们只需要一直把满足条件的val维护就行。最后的时候再跑一次check函数就行。
D题代码
#include<iostream> #include<stdio.h> #include<string.h> #include<algorithm> #include<math.h> #include<map> using namespace std; const int maxx = 2e5+10; int a[maxx]; int cnt[maxx]; int n,k; int ans[maxx]; int anss; bool check(int val) { int num=0; int tot =1; for (int i=1; i<maxx; i++) { for (int j=val; j<=cnt[i]; j+=val) { num++; ans[tot++]=i; } } if (num>=k)return 1; else return 0; } void fen(int l,int r) { int mid=(l+r)/2; if (l<=r) { if (check(mid)) { anss=mid; fen(mid+1,r); } else { fen(l,mid-1); } } return; } int main() { scanf("%d%d",&n,&k); memset(cnt,0,sizeof(cnt)); for (int i=1; i<=n; i++) { scanf("%d",&a[i]); } int l=1; int r=0x3f3f3f3f; for (int i=1; i<=n; i++) { cnt[a[i]]++; } fen(l,r); check(anss); for (int i=1;i<=k; i++) { if (i!=k)printf("%d ",ans[i]); else printf("%d\n",ans[k]); } return 0; } /* */
后面留坑
E题 每天组织一场比赛,后一天的比赛题目数是前一天比赛数目的两倍,并且每天的题目必须是一样,并且第一天题目可以自己选,问最大选题的数目是多少。
这道题我最开始理解错题意了,后来然后也没有想到,最后读懂题目,发现没办法做。其实我们发现,要把次数用map统计,这并没有什么问题,然后把这些值进行离散化,也就是把每种数存下来就行。然后再用一个数组存这些数出现的次数,排序,然后用low_bound查找需要次数。最后递增就行
给出n道有类型的题目,每天组织一场专题比赛,该天题目数量必须是前一天的2倍,第一天的题目数量可以任意选择,求能消耗的最多题目数量
1 #include<iostream> 2 #include<string.h> 3 #include<stdio.h> 4 #include<algorithm> 5 #include<map> 6 using namespace std; 7 int b_size; 8 int a_size; 9 map<int,int>p; 10 vector<int>a,b; 11 int check(int x) 12 { 13 int sum=0; 14 int pos=0; 15 while(1) 16 { 17 pos=(lower_bound(b.begin()+pos,b.end(),x)-b.begin());//大于或等于val的第一个元素位置 18 if (pos==a_size)break;//如果都比这个数小那么返回的pos==数组大小 19 pos++; 20 sum+=x; 21 x*=2; 22 } 23 return sum; 24 } 25 int main() 26 { 27 int n; 28 int tmp; 29 int mx=0; 30 p.clear(); 31 scanf("%d",&n); 32 for (int i=1; i<=n; i++) 33 { 34 scanf("%d",&tmp); 35 if (p[tmp]==0) 36 { 37 a.push_back(tmp); 38 } 39 p[tmp]++; 40 mx=max(mx,p[tmp]); 41 } 42 b_size=a_size=a.size(); 43 for(int i=0; i<a_size; i++) 44 { 45 b.push_back(p[a[i]]); 46 } 47 sort(b.begin(),b.end()); 48 int ans=0; 49 for (int i=1; i<=mx; i++) 50 { 51 ans=max(ans,check(i)); 52 } 53 printf("%d\n",ans); 54 return 0; 55 }
F1 简单DP,用DP[i][j]代表前i位置,选j元素的最大值。
转移方程dp[i][l+1]=max(dp[i][l+1],dp[j][l]+a[i])
表示前i个元素,选取了l+1个元素,我们需要从这个数的前i-k个元素,选取了l个元素传递过来。
代码如下:
#include<iostream> #include<stdio.h> #include<string.h> #include<algorithm> #define ll long long using namespace std; ll a[245]; ll dp[250][250]; const ll inf = 0x3f3f3f3f; int main() { ll n,k,x; scanf("%lld%lld%lld",&n,&k,&x); { for(int i=1; i<=n; i++) { scanf("%lld",&a[i]); } memset(dp,-inf,sizeof(dp)); dp[0][0]=0; //dp[i][j]前i个选出j个 for(int i=1; i<=n; i++)//前i个选出l+1个 { for(int j=i-1; j>=max((ll)0,i-k); j--)//从前i-k位到i-1位,选出l个加上i位置选出a[i] { for(int l=0; l<x; l++) dp[i][l+1]=max(dp[i][l+1],dp[j][l]+a[i]); } } ll maxn; maxn=-1; for (int i=n; i>n-k; i--) { maxn=max(dp[i][x],maxn); } printf("%lld\n",maxn); } return 0; }