T1 小苹果
题目描述
理论分析
对于第一问,我们按照题意模拟每天取走的是多少个苹果即可。由于每天可以取走原来的,数据范围没次会降低到,也就是说这样的过程的时间复杂度可以用下式表示:
对于本题的数据范围n<1e9,这个时间复杂度计算后在1e5左右,是可以接受的。
对于第二问,首先有一个很显然的结论是:第n个苹果会存在于从第一天开始的连续的若干天,然后在后面的天里不再存在。因此,我们可以维护一个bool类型的变量,作为最后一个苹果还在不在的判别。然后在每一天的判断过程中,我们通过整除关系可判别,这个苹果是不是被拿走。
代码实现
#include <bits/stdc++.h>
using namespace std;
int main(){
int n;
cin>>n;
int day=0, last=0;
bool flag=false;
while(n){
day++;
//是不是这天拿最后一个
if(!flag&&(n-1)%3==0) flag=true, last=day;
//这天结束,剩的苹果数
n=n-(n-1)/3-1;
}
cout<<day<<" "<<last<<endl;
return 0;
}
T2 公路
题目描述
理论分析
贪心即可!对于这个题目来说,一共要走的路程是一定的,所以一共加多少油也是确定的。不存在选择某种加油方案剩余油多,而选择另外的方案剩余油少的问题。因此,我们要做的就是让每升油的油价尽可能的低。那么问题来了,我们能不能都用最低油价买油呢?显然,当开始的0号位置油价最低时(题目中的1号,这里直接对应代码的编号说明了!下同),我们可以办到这件事,但是当1号节点油价并不是最低价的时候,我们需要首先走到油价最低的站点,那就至少需要在前面的节点买油。也就是说,假设处的油价最低,我们的问题就演变为了,首先求1号点到最低花多少钱,然后后半段直接用处的油价走完就行。那前半程的求解是一个数据范围比原问题更小的同类问题的求解。
我们当然可以像上述那样做递归的求解程序,只不过这样的时间效率是不高的,最差会来到,只能通过一半左右的样例。实际上,上述贪心的过程等价于下面的贪心:我们从前往后考虑每个节点,维护需求加油的数量(只保证可以走到下一个节点)num,加完油后走到下个节点后会剩下多少油last,以及历史(已经过节点)的最低油价mn。对于每个节点,我们花费num*mn保证可以走到下一个节点。(也就是说,我们在拥有历史最低油价的那个点在原来的基础上多加num升油保证可以走到下一个点)。
代码实现
#include <bits/stdc++.h>
#define int long long
using namespace std;
signed main(){
int n, d;
cin>>n>>d;
int last=0;
vector<int> v(n);
vector<int> a(n);
for(int i=0; i<n-1; i++) cin>>v[i];
int ans=0, mn=0x3f3f3f3f, num;
for(int i=0; i<n-1; i++){
cin>>a[i];
// 历史低价
mn=min(mn, a[i]);
// 至少需要多少才能走到下一个点
num=(v[i]-last+d-1)/d;
ans=ans+num*mn;
// 油余量够走多远
last=num*d-v[i]+last;
}
cout<<ans<<endl;
return 0;
}
T3 一元二次方程
题目描述
理论分析
按照题意 老老实实模拟即可。需要注意的细节稍微有点多,但是细心一点也是可以一遍AC的!!
细节一:分数输出需要保证分母不是负数。
细节二:对于有两个解的情况而言,题中已经提示过中的,可以利用这一点简化代码的书写。
细节三:根号的化简要从大到小试探因数,这样更容易保证不重不漏。
细节四:根号内如果是完全平方数,那么最终结果将会不带有 “sqrt()” ,此时答案的化简需要自己分析好。
细节五:格式的答案,要注意格外第一项是不是0的判断。
大概就是这些细节吧。。。时间复杂度
代码实现
#include <bits/stdc++.h>
#define int long long
using namespace std;
void print_sqrt(int s, int b, int a){
int g=__gcd(b, a);
pair<int, int> p;
p.first=b/g;
p.second=a/g;
if(p.first<0) p.first*=-1;
if(p.second<0) p.second*=-1;
if(p.first!=1) cout<<p.first<<"*";
cout<<"sqrt("<<s<<")";
if(p.second!=1) cout<<"/"<<p.second;
}
void print_int(int b, int a){
int g=__gcd(b, a);
pair<int, int> p;
p.first=b/g;
p.second=a/g;
if(p.second<0){
p.first*=-1;
p.second*=-1;
}
if(p.second==1) cout<<p.first;
else cout<<p.first<<"/"<<p.second;
}
void solve(){
int a, b, c, g;
cin>>a>>b>>c;
int delta=b*b-4*a*c;
if(delta<0){
cout<<"NO\n";
}
else if(delta==0){
if(b==0) cout<<"0\n";
else{
print_int(-b, 2*a);
cout<<"\n";
}
}
else {
bool flag=false;
int num=1;
for(int i=min(1000ll, delta-1); i>0; i--){
if(i*i==delta){
delta/=(i*i);
num*=i;
break;
}
else if(i*i<delta && delta%(i*i)==0){
num*=i;
delta/=(i*i);
}
}
if(delta==1){
if(a<0) print_int(-b-num, 2*a);
else print_int(-b+num, 2*a);
cout<<"\n";
}
else {
if(b!=0) {
print_int(-b, 2*a);
cout<<"+";
}
print_sqrt(delta, num, 2*a);
cout<<"\n";
}
}
}
signed main(){
int n, m;
cin>>n>>m;
while(n--){
solve();
}
return 0;
}
T4 旅游巴士
题目描述
理论分析
这是一个分层图问题,即图上每个点要拆成个点。拆点的依据是原本的点 意义下的到达时间。我们使用表示到第i个点满足时间 的最短时间。初始条件为 ,然后跑最短路就行了。
相对难处理的点在于每条边经过的时间限制,对于这一点,我们可以原地等待k的倍数时间达成(其实相当于出发时间晚了k的倍数时间)。
代码实现
#include <bits/stdc++.h>
#define int long long
using namespace std;
int ans[20005][100];
vector<vector<pair<int, int>>> g;
signed main(){
int n, m, k;
cin>>n>>m>>k;
g.resize(n+1);
int u, v, a;
while(m--){
cin>>u>>v>>a;
g[u].push_back(pair<int, int> (v, a));
}
for(int i=1; i<=n; i++)
for(int j=0; j<k; j++) ans[i][j]=0x7f7f7f7f7f7f7f7f;
queue<pair<int, int>> q;
q.push(pair<int, int> (1, 0));
ans[1][0]=0;
int now, pos, need;
while(!q.empty()){
auto t=q.front();
q.pop();
for(int i=0; i<g[t.first].size(); i++){
now=g[t.first][i].first;
pos=(t.second+1)%k;
need=ans[t.first][t.second];
if(need<g[t.first][i].second) need=need+(g[t.first][i].second-need+k-1)/k*k;
need++;
if(ans[now][pos]==-1||need<ans[now][pos]){
// cout<<now<<" "<<pos<<" "<<need<<endl;
ans[now][pos]=need;
q.push(pair<int, int> (now, pos));
}
}
}
if(ans[n][0]>=0x7f7f7f7f7f7f7f7f) cout<<-1<<endl;
else cout<<ans[n][0]<<endl;
return 0;
}