7.8
C题--Sequence Decomposing
求有多少个上升子序列,如果纯暴力将会超时,所以使用二分去搜。
#include<bits/stdc++.h>
using namespace std;
//#define int long long
int a[100005],b[100005],t=1;
signed main(){
int n,a[100005];
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=1;i<=n;i++){
int temp=upper_bound(b+1,b+t,a[i],greater<int>())-b;
if(temp==t){
b[t++]=a[i];
}
else{
b[temp]=a[i];
}
}
cout<<t-1<<endl;
}
B题--equeue
对于一个序列有四种处理方式,分别为从左边取,从右边取,放回右边,放回左边。给定你操作次数之后,分别选择上述方式,最终使自己手里剩余最多。
贪心解决,先去决定分别从左右拿的次数,然后用剩余次数去确定从左右两侧放回的次数,最后比较每次手中剩余的数大小,选出最大的那个数。
#include<bits/stdc++.h>
using namespace std;
int n,k,v[55];
int s[105];
int i,j,sum,ia,ib,maxx;
int main(){
std::ios::sync_with_stdio(0);
std::cin.tie(0);
cin>>n>>k;
for(i=1;i<=n;i++){
cin>>v[i];
}
for(ia=0;ia<=min(n,k);ia++){
for(ib=0;ib<=min(n,k)-ia;ib++){
j=0;
sum=0;
for(i=1;i<=ia;i++){
s[++j]=v[i];
sum+=v[i];
}
for(i=1;i<=ib;i++){
s[++j]=v[n-i+1];
sum+=v[n-i+1];
}
sort(s+1,s+1+j);
for(int i=1;i<=min(j,k-ia-ib);i++){
if(s[i]>0) break;
sum-=s[i];
}
maxx=max(maxx,sum);
}
/*for(i=1;i<=min(j,k-ia-ib);i++){
if(s[i]>0) break;
sum-=s[i];
}
maxx=max(maxx,sum);*/
}
cout<<maxx<<endl;
return 0;
}
F题--Integer Cards
为保证结果最大,则可以将原数组进行排序,也将要替换的数组进行排序,将原数组中最小的数用替换数组中最大的数进行替换,知道所替换的数组小于原数组时停止替换(确保差值最大),然后进行加和运算。
#include<bits/stdc++.h>
using namespace std;
#define int long long
priority_queue<int,vector<int>,greater<int> > q;
int n,m;
struct node{
int bb,cc;
}e[100005];
bool cmp(node x,node y){
return x.cc>y.cc;
}
signed main(){
std::ios::sync_with_stdio(0);
std::cin.tie(0);
cin>>n>>m;
for(int i=0;i<n;i++){
int x;
cin>>x;
q.push(x);
}
for(int i=0;i<m;i++){
cin>>e[i].bb>>e[i].cc;
}
sort(e,e+m,cmp);
for(int i=0;i<m;i++){
while(e[i].bb>0){
if(e[i].cc<=q.top()){
break;
}
q.push(e[i].cc);
q.pop();
e[i].bb--;
}
}
int sum=0;
for(int i=0;i<n;i++){
sum+=q.top();
q.pop();
}
cout<<sum<<endl;
}
在写题的时候,总是只会写最简单的想法,不会如何优化,导致超时,以后要多写去练。
7.9
B题--Consecutive
在一个字符串中取一个字串,然后观察字串中是否有连续的两个相同的数字,进行输出。
开始的时候没有注意到时间,选择了两重循环来解,会超时。实际可以开两个数组,在开始时就处理。
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod = 1e9 + 7;
int temp[300005];
int ans[300005];
signed main(){
std::ios::sync_with_stdio(0);
std::cin.tie(0);
int n,q;
int a,b;
int sum=0;
string s;
cin>>n>>q;
cin>>s;
for(int i=0;i<s.size()-1;i++){
if(s[i]==s[i+1]) temp[i]=1;
else temp[i]=0;
ans[i+1]=ans[i]+temp[i];
}
for(int i=0;i<q;i++){
cin>>a>>b;
cout<<ans[b-1]-ans[a-1]<<endl;
}
return 0;
}
A题--Sierpinski carpet
在比赛的时候读错了题意。以为只有中间的那个才会输出".",其余都是"#",所以交的几发都wa了。实际上,可以将其大的图案分成九宫格来看,每个九宫格的中心都会输出".",其余的都是”#“,然后再将四周的八个小格子,看成每个独立的小九宫格,将每一个小九宫格的中心变为”.",依此类推。
#include<bits/stdc++.h>
#include<math.h>
using namespace std;
#define int long long
signed main(){
std::ios::sync_with_stdio(0);
std::cin.tie(0);
int n;
char sum[735][735];
cin>>n;
//cout<<"pow="<<pow(3,n)<<endl;
for(int i=1;i<=pow(3,n);i++){
for(int j=1;j<=pow(3,n);j++){
sum[i][j]='#';
//cout<<sum[i][j];
}
//cout<<endl;
}
for(int k=1;k<=n;k++){
for(int ia=1+pow(3,k-1);ia<=pow(3,n);ia+=pow(3,k)){
for(int ib=1+pow(3,k-1);ib<=pow(3,n);ib+=pow(3,k)){
for(int i=ia;i<ia+pow(3,k-1);i++){
for(int j=ib;j<ib+pow(3,k-1);j++){
sum[i][j]='.';
}
}
}
}
}
for(int i=1;i<=pow(3,n);i++){
for(int j=1;j<=pow(3,n);j++){
cout<<sum[i][j];
//cout<<'1';
}
cout<<endl;
}
//cout<<"pow="<<pow(3,n);
}
C题--Minimum Width
可以将数组进行排序,然后进行二分,查找最小值。
#include<bits/stdc++.h>
#include<math.h>
using namespace std;
#define int long long
int n,m;
int a[200005];
int l,r,mid;
int mm=0;
int temp(int x){
int sum=-1,num=1;//sum的初值设为负数,保证可以消去第一个单词所带的空格,num相当于计数器,确定行数
for(int i=1;i<=n;i++){
if(sum+1+a[i]<=x){
sum=sum+a[i]+1;
}
else{
sum=a[i];
num++;
}
}
if(sum==-1) num--;//一行都没使用
if(num<=m) return 1;
else return 0;
}
signed main(){
std::ios::sync_with_stdio(0);
std::cin.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i];
mm=max(mm,a[i]);
}
l=mm-1;
r=1e15+1;
while(l+1<r){
mid=(l+r)/2;
if(temp(mid)==1){
r=mid;
}
else{
l=mid;
}
}
cout<<r<<endl;
}
D题--Printing Machine
一个物品在Ti秒进入打印机,在打印机上停留Di秒,打印机可以立即打印但每次打印后需要停留1秒后进行下一次工作。(相当于打印机工作一次需要一秒)
将每一个进入打印机时间相同的书同时压入vector中,然后将每一个结束时间跟打印机的工作时间相比,如果结束时间小于打印机的工作时间,则证明打印机无法打印该物体。
#include<bits/stdc++.h>
#include<math.h>
using namespace std;
#define int long long
int n,l,r;
int ans=0;
vector<pair<int,int>> sum;
signed main(){
std::ios::sync_with_stdio(0);
std::cin.tie(0);
cin>>n;
for(int i=0;i<n;i++){
cin>>l>>r;
sum.emplace_back(l,r);
}
sort(sum.begin(),sum.end());
int i=0;
priority_queue<int,vector<int>,greater<int> > p;
for(int t=0; ;t++){
if(p.empty()){
if(i==n) break;
t=sum[i].first;
}
while(i<n&&t==sum[i].first){
p.push(sum[i].first+sum[i].second);
i++;
}
while(p.size()&&p.top()<t) p.pop();
if(p.empty()==0){
ans++;
p.pop();
}
}
cout<<ans<<endl;
}
二分训练--Music Notes
每一个音符都存在一段时间而且有先后顺序,所以可以使用二分查找。
#include<bits/stdc++.h>
using namespace std;
int n,q;
long long num[50005],aa,bb;
int main()
{
cin>>n>>q;
for(int i=0;i<n;i++)
{
cin>>aa;
if(i==0) num[i]=aa;
else num[i]=num[i-1]+aa;
}
while(q--)
{
cin>>bb;
long long tmp=bb+1;
int ans=lower_bound(num,num+n,tmp)-num+1;
cout<<ans<<endl;
}
return 0;
}
二分训练--完全平方数
完全平方数从小到大一次递增,所以可以使用二分查找小于这个数中究竟有几个完全平方数,最后r-(l-1)即为答案。
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod = 1e9 + 7;
int ff=1;
int temp(int x){
int l=0,r=x;
//cout<<"l="<<l<<endl;
//cout<<"r="<<r<<endl;
int mid;
while(l<=r){
mid=(l+r)/2;
//cout<<"l="<<l<<endl;
//cout<<"r="<<r<<endl;
//cout<<"mid="<<mid<<endl;
int kk=mid*mid;
if(kk<=x){
l=mid+1;
}
else{
r=mid-1;
}
}
return l;
}
signed main(){
std::ios::sync_with_stdio(0);
std::cin.tie(0);
int a,b,mid,l,r,n;
cin>>n;
for(int i=0;i<n;i++){
cin>>a>>b;
l=temp(a-1);
//cout<<"l="<<l<<endl;
r=temp(b);
cout<<r-l<<endl;
//cout<<"r="<<r<<endl;
}
}
二分训练--跳石头
由于最短距离可以通过左右两端来回逼近,所以使用二分查找不断去逼近最短距离的最大值。
#include<bits/stdc++.h>
using namespace std;
int l,n,m;
vector<int> ve;
bool check(int x)
{
int cnt=0;
int now=0;
for(int i=1;i<=n+1;)
{
if(ve[i]-ve[now]<x)
{
cnt++;
//now=i+1;
i++;
}
else
{
now=i;
i++;
}
}
return cnt<=m;
}
int main()
{
cin>>l>>n>>m;
ve.push_back(0);
for(int i=0;i<n;i++)
{
int num;
cin>>num;
ve.push_back(num);
}
ve.push_back(l);
int l=1,r=1000000000;
while(l<r)
{
int mid=(l+r+1)>>1;
if(check(mid))
{
l=mid;
}
else
{
r=mid-1;
}
}
cout<<l;
return 0;
}
7.10
B题--抱歉
满足每个点都必须有两个跟他连接,所以n各点构成m个平面所需要最少的边n+m-2(n>=2);当所需平面数为一时,则不需要边;当只有一个点时,构成的m个平面所需边数位m-1;
#include<stdio.h>
int main(){
long long n,m;
scanf("%lld %lld",&n,&m);
while(n!=0&&m!=0){
long long sum;
sum=n+m-2;
if(m==1){
sum=0;
}
else if(n==1){
sum=m-1;
}
printf("%lld\n",sum);
scanf("%lld %lld",&n,&m);
}
return 0;
}
C题--搬寝室
就是要让每次的左右手的差最小,比赛的时候没有将情况考虑清楚,所以交的几次全是错的。实际上,需要找出一个动态方程 dp[j] = min(dp[j-1], dp[j]+(a[2*i-2+j]-a[2*i+j-1])*(a[2*i-2+j]-a[2*i+j-1]));每次找到当前剩余的物品中,能让左右手差最小的数,然后依次累加。
还要注意的是他是每次输入,不是只输一次。
#include<bits/stdc++.h>
using namespace std;
//#define int long long
const int mod = 1e9 + 7;
int mm=INT_MAX;
signed main(){
std::ios::sync_with_stdio(0);
std::cin.tie(0);
int n,k;
int a[2005];
long long f[2005][1005];
// cin>>n>>k;
while(cin>>n>>k){
for(int i=1;i<=n;i++) cin>>a[i];
sort(a+1,a+1+n);
for(int i=0;i<=n;i++){
for(int j=0;j<=k;j++){
if(j==0) f[i][j]=0;
else f[i][j]=mm;
}
}
for(int i=2;i<=n;i++){
for(int j=1;j<=min(k,i);j++){
f[i][j]=min(f[i-1][j],f[i-2][j-1]+(a[i]-a[i-1])*(a[i]-a[i-1]));
}
}
cout<<f[n][k]<<endl;}
}
I题--To 3
即是将每个数位上的数都对三进行求余,将每一个不能整除的数统计下来,并进行求和,如果求和后的数可以整出三,则整体可以,不能的话分类讨论。
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod = 1e9 + 7;
signed main(){
std::ios::sync_with_stdio(0);
std::cin.tie(0);
string s;
cin>>s;
int a[20];
int sum=0;
int j=0;
int ll=0;
int ee=0;
for(int i=0;i<s.size();i++){
int kk=s[i]-'0';
if(kk%3!=0){
a[++j]=kk%3;
sum+=a[j];
if(sum%3==0){
j=0;
}
}
}
for(int i=1;i<=j;i++){
if(a[i]%3==1) ll++;
else if(a[i]%3==2) ee++;
}
int ans=sum%3;
if(ans==0){
cout<<'0'<<endl;
}
else if(ans==1){
if(ll>=1) {
//cout<<"jin1"<<endl;
if(s.size()-ll==0){
cout<<"-1"<<endl;
}
else {
//cout<<"jin2"<<endl;
cout<<'1'<<endl;}
}
else{
if(s.size()-j==0){
cout<<"-1"<<endl;
}
else {
//cout<<"jin10"<<endl;
cout<<j<<endl;}
}
}
else{
if(ee>=1){
//cout<<s.size()<<' '<<j<<endl;
if(s.size()-ee==0){
cout<<"-1"<<endl;
}
else cout<<'1'<<endl;
}
else if(ee==0&&ll>=2){
if(s.size()-j==0){
cout<<"-1"<<endl;
}
else cout<<'2'<<endl;
}
else{
if(s.size()-j==0){
cout<<"-1"<<endl;
}
else cout<<s.size()-j<<endl;
}
}
}
二分训练--Greedy Gift Takers
二分查找一个循环,在这个循环内的每一头牛都能领到礼物,而循环之外的牛领不到任何礼物。
使用二分法不断逼近最后一头在循环内的牛。
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod = 1e9 + 7;
int n,a[100005];
int b[100005];
bool temp(int x){
for(int i=1;i<=x;i++){
b[i]=0;
}
for(int i=1;i<=x;i++){
b[a[i]]++;
}
for(int i=1;i<=x;i++){
b[i]+=b[i-1];
if(b[i]>=i) return true;
}
return false;
}
signed main(){
std::ios::sync_with_stdio(0);
std::cin.tie(0);
cin>>n;
for(int i=1;i<=n;i++){
int x;
cin>>x;
a[i]=n-x;
}
int l=1,r=n,mid;
while(l<r){
mid=(l+r)/2;
if(temp(mid)) r=mid;
else l=mid+1;
}
cout<<n-l<<endl;
}
二分训练--Chocolate Eating
不断二分去查找最小幸福值,当大于等于最小幸福值的时候就进行除二,进入新的一天,继续进行累加以此类推。
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod = 1e9 + 7;
int n,d;
int a[50005];
int ans[50005];
bool temp(int x){
int cnt=0;
int cc=1;
for(int i=1;i<=d;i++){
while(cnt<x&&cc<=n){
cnt+=a[cc];
ans[cc]=i;
cc++;
}
if(cnt<x) return false;
cnt/=2;
}
while(cc<=n){
ans[cc]=d;
cc++;
}
return true;
}
signed main(){
std::ios::sync_with_stdio(0);
std::cin.tie(0);
cin>>n>>d;
for(int i=1;i<=n;i++) cin>>a[i];
int l=1,r=1e15;
int mid;
while(l<=r){
mid=(l+r)/2;
if(temp(mid)==true) l=mid+1;
else r=mid-1;
}
cout<<l-1<<endl;
temp(l-1);
for(int i=1;i<=n;i++){
cout<<ans[i]<<endl;
}
}
7.11
二分训练--华华给月月准备礼物
二分查找木棍的最大值,看每根木棍可以分成多少个最大值的木棍,然后与所需要的杆子数进行比较,最后找出最大值。
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod = 1e9 + 7;
int n,k;
int a[200005];
int mm=0;
bool temp(int x){
int cnt=0;
for(int i=1;i<=n;i++){
cnt+=(a[i]/x);
}
//cout<<"cnt="<<cnt<<endl;
if(cnt>=k) return true;
else return false;
}
signed main(){
std::ios::sync_with_stdio(0);
std::cin.tie(0);
cin>>n>>k;
for(int i=1;i<=n;i++){
cin>>a[i];
mm=max(mm,a[i]);
}
int l=1,r=mm;
//cout<<mm<<endl;
int mid;
while(l<=r){
mid=(l+r)/2;
//cout<<"mid="<<mid<<endl;
if(temp(mid)) l=mid+1;
else r=mid-1;
}
cout<<r;
}
二分训练--扑克牌
二分查找。假设能凑成mid套牌,每一个不够mid的牌都会用j这个万能牌来代替,算出总共需要多少张万能牌,需要的万能牌的数目不能超过j的总数也不能超过mid(超过mid则证明有一套牌中使用了两次j,不合题意需要舍去)
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod = 1e9 + 7;
int n,m;
int c[55];
int temp(int x){
int cnt=0;
for(int i=1;i<=n;i++){
int k=x-c[i];
if(k>0) cnt+=k;
}
if(cnt<=m&&cnt<=x) return true;
else return false;
}
signed main(){
std::ios::sync_with_stdio(0);
std::cin.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>c[i];
}
int l=0,r=1e9+10;
int mid;
while(l<=r){
mid=(l+r)/2;
if(temp(mid)) l=mid+1;
else r=mid-1;
}
cout<<l-1<<endl;
}
二分训练--借教室
不仅需要用到二分还需要用到差分的思想,算出每天提供的教室与前一天提供的教室之间的数量差。然后计算差分的总和,当出现负数时则证明该天教室不够分。
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod = 1e9 + 7;
int n,m;
int num[1000005],ans[1000005];
struct node{
int d,str,end;
};
vector<node> ve;
bool temp(int x){
int ans1[1000005];
for(int i=1;i<=n;i++) ans1[i]=ans[i];
for(int i=1;i<=x;i++){
node nn=ve[i-1];
ans1[nn.str]-=nn.d;
ans1[nn.end+1]+=nn.d;
}
int sum=0;
for(int i=1;i<=n;i++){
sum+=ans1[i];
if(sum<0) return false;
}
return true;
}
signed main(){
std::ios::sync_with_stdio(0);
std::cin.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>num[i];
ans[i]=num[i]-num[i-1];
}
for(int i=1;i<=m;i++){
node now;
cin>>now.d>>now.str>>now.end;
ve.push_back(now);
}
int l=0,r=m;
int mid;
while(l<=r){
mid=(l+r)/2;
if(temp(mid)) l=mid+1;
else r=mid-1;
}
//cout<<l<<endl;
if(l>=1&&l<=m){
cout<<"-1"<<endl;
cout<<l<<endl;
}
else cout<<'0'<<endl;
}
B题--Linear Approximation
https://vjudge.net/problem/AtCoder-arc100_a/origin
相当于先将数组中的元素都减去自己的下标,然后将其重新排序,然后寻找中位数(相当于在数轴上寻找一个点,使其到各点的距离总和最小)
#include<bits/stdc++.h>
#define int long long
#define endl "\n"
using namespace std;
int a[200005];
signed main()
{
int n;
//int a[200005];
cin>>n;
for(int i=1;i<=n;i++){
int k;
cin>>k;
a[i]=k-i;
}
sort(a+1,a+1+n);
int num=0;
if(n==1) num=a[n];
else num=(a[(n+1)/2]);
int sum=0;
for(int i=1;i<=n;i++){
sum+=abs(a[i]-num);
}
cout<<sum<<endl;
}
C题--Minimization
https://vjudge.net/problem/AtCoder-arc099_a/origin
和最小数1出现的位置无关,之与N和K有关,比赛的时候没有考虑到k,只考虑了N。
#include<bits/stdc++.h>
#define int long long
#define endl "\n"
using namespace std;
signed main()
{
int n,k;
int a[100005];
cin>>n>>k;
for(int i=1;i<=n;i++){
cin>>a[i];
}
int l=0,cnt=0;
while(l<n){
if(l==0) l+=k;
else l+=k-1;
cnt++;
}
cout<<cnt<<endl;
}