1.数的计算
题目其他部分:
这道题目可以先试着自己推一下找一下规律,题目给出的数字是一个小于等于一千的数字,这里我们不妨考虑一下从小往大推,我们从1开始,用f[i]来表示对应i共有几个合法的序列:
当n为1的时候,我们可以得到序列1,f[1]=1;
当n为2的时候,我们可以得到序列2 21,f[2]=2=f[1]+1;
当n为3的时候,我们可以得到的序列为:3 31,f[3]=2=f[1]+1;
当n为4的时候,我们可以得到的序列为:4 42 41 421,f[4]=4=f[1]+f[2]+1;
当n为5的时候,我们可以得到的序列为:5 52 51 521,f[5]=f[1]+f[2]+1;
到这里其实已经可以发现规律,每个i对应的函数f值都等于从1加到i的一半所对应的函数值再加上1.
所以代码这么写:
#include<iostream>
#include<cmath>
using namespace std;
int n;
const int N=1e3+10;
int ans;
int f[N];
int main(){
cin>>n;
for(int i=1;i<=n;i++){
for(int j=1;j<=i/2;j++){
f[i]+=f[j];
}
++f[i];
}
ans=f[n];
cout<<ans<<endl;
return 0;
}
这个代码的时间复杂度虽然看起来较高,但是由于n的范围是小于等于1000的所以无论如何都不会超时。
2.金属冶炼:
这道题可以直接通过数学推导的过程进行解答:首先我们不妨假设答案是v,那么对于一组金属的a,b必有b*v<=a,并且容易推测出冶炼不出b+1个,所以还有(b+1)* v>a;
通过移项之后,有:v <= a/b,v > a/(b+1);
所以对于每一组a,b我们能判断出对应的最大值和最小值,那么要让答案满足所有条件,必须使得算出来的最小值以max来更新,算出来的最大值以min来更新,代码如下:
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n;
signed main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
cin>>n;
int ans1=1>>30,ans2=1<<30;
for(int i=1;i<=n;i++){
int a,b;
cin>>a>>b;
ans1=max(ans1,a/(b+1)+1),ans2=min(ans2,a/b);
}
cout<<ans1<<" "<<ans2<<endl;
return 0;
}
这道题目找到正确的方法就会很快做出,当然也能通过二分的方法去做。
3.管道:
先进行分析,我们要求的是最小的t,并且t满足t时刻已经让所有管道都检测到了水,那么我们就可以发现,t其实就相当于是一个分界线,小于t的都不满足条件,大于t的都满足条件,而t时刻是刚刚好满足的,也就是我们要求的条件。
所以接下来我们就可以考虑使用二分的方法来算出这个临界的t,接下来看代码:
#include<iostream>
#include<vector>
#include<cstring>
#include<algorithm>
#define int long long
using namespace std;
const int N=1e6+100;
typedef pair<int,int> pii;
pii a[N];
int n,len;
bool check(int x){
vector<pii>p;
for(int i=0;i<n;i++){
int l=a[i].first,s=a[i].second;
if(s>x) continue;
int t1=max(1ll,l-x+s),t2=min(len,l+x-s);
p.push_back({t1,t2});
}
int num=p.size();
sort(p.begin(),p.end());
if(p.empty()) return false;
if( p[0].first>1 ) return false;
int pd=p[0].second;
for(int i=1;i<num;i++){
if(p[i].first > pd+1) return false;
pd=max(pd,p[i].second);
}
return pd == len;
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
cin>>n>>len;
for(int i=0;i<n;i++){
int x,y;
cin>>x>>y;
a[i].first=x;
a[i].second=y;
}
int l=0,r=2e9;
while(l<r){
int mid=l+r>>1;
if(check(mid)) r=mid;
else l=mid+1;
}
cout<<l<<endl;
return 0;
}
4.壁画:
读了三遍题目才把题意完全的读懂,对于这一道题,先手进行选择一个地方进行作画,然后作一次画就会坍塌一面墙壁,并且题目中说坍塌的墙壁附近有且仅有一面还没有坍塌的墙壁,因此我们可以发现,其实题目要我们求的就是长度为n/2的最大前缀和,那么代码就可以这样去写:
#include<iostream>
#include<algorithm>
#include<vector>
#include<string>
#include<cstring>
#define int long long
typedef unsigned long long ull;
using namespace std;
int t;
int n;
signed main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
cin>>t;
for(int k=1;k<=t;k++){
cin>>n;
string s;
cin>>s;
s=' '+s;
vector<int>a(n+1);
int ans=0;
for(int i=1;i<=n;i++){
a[i]=a[i-1]+(s[i]-'0');
}
int cnt=(n+1)/2;
for(int i=cnt;i<=n;i++){
ans=max(ans,a[i]-a[i-cnt]);
}
cout<<"Case #"<<k<<": "<<ans<<endl;
}
return 0;
}
对于每一组数据都单独进行处理,注意一下cnt一定要加1再除以2,因为我们先手进行的是作画操作,如果有奇数个墙壁那么作画的次数必定比坍塌的墙壁数目多1。
5.公交换乘:
这道题目只是需要有贪心的思想去做就好,优化时间复杂度到能获得满分即可,对于每一张票我们可以用结构体表示它的使用状态和价格以及购买时间,来方便进行模拟,以下即代码实现:
#include<bits/stdc++.h>
using namespace std;
int n,m,ans;
struct ticket{
int time,price;
bool used;
}a[100010];
int main(){
cin>>n;
for(int i=1;i<=n;i++){
int way,p,t;
cin>>way>>p>>t;
if(way==0){
ans+=p;
m++;
a[m].time=t;
a[m].price=p;
a[m].used=false;
} else {
int id=-1;
for(int j=m;j>=1;j--){
if(a[j].time<t-45) break;
if(a[j].price>=p && !a[j].used)
id=j;
}
if(id!=-1)
a[id].used=true;
else ans+=p;
}
}
cout<<ans<<endl;
return 0;
}
感谢您的观看。