3C
模拟、递推(规律)、递归、枚举、贪心
这些思想我在解题的时候要主动去思考,比如说这个地方一开始卡住了,根本没有主动去想枚举的事情,但是实际上这个题目就是可以用来模拟操作然后枚举的,但是这里也有小技巧:
我们很容易想到枚举a,然后枚举b(这里b的最小值是b本身的限制和c的最小值减去a的最小值的最大值),然后看看能不能够k个,但是这样时间复杂度有点高,对于这种确定的关系(a + b = c)(有时候可能不是和),我们尝试能不能通过减少枚举对象,来枚举出我们想要的答案:
当然是可以的,我们可以仅仅枚举a,然后算出b的最小范围和最大范围,然后这样就得出来了a + b的范围,然后我们和c进行比较,取一个交集,这个就是能算出来的范围,如果此时此刻大于k了,我们就可以输出了,如果没有大于k,我们就接着枚举下一个数字,我们的for循环当中,只出现了一个a,这样的时间复杂度肯定是可以的
代码中注意的地方
我们在计算几位数字最小是几的时候开一个数组,避免频繁的使用pow
注意到交集的时候可能是空集,也就是负数,我们此时此刻不计入计数之内,所以需要和零获得一个max,但是0会被认为是一个int 所以加上ll,这个写法注意一下
#include<bits/stdc++.h>
using namespace std;
#define int long long
typedef long long ll;
void solve(){
int mn[10]={0,1,10,100,1000,10000,100000,10000000};
int a,b,c,k;
cin>>a>>b>>c>>k;
int ans=0;
for(int i=mn[a];i<10*mn[a];i++){ //枚举a
int l=i+mn[b],r=i+10*mn[b]-1; //计算出可到达的c
l=max(l,mn[c]); //和c的范围取交集
r=min(r,mn[c]*10-1);
ans += max(0ll,r - l + 1);
if(ans>=k){
int tmp=ans-k; //计算比目标状态大多少,用r减去
cout<<i<<' '<<'+'<<' '<<r-tmp-i<<" = "<<r-tmp<<endl;
return;
}
}
cout<<-1<<endl;
return;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int T=1;
cin>>T;
while(T--){
solve();
}
return 0;
}
B
这个题目刚开始没有思路,补完题目,我只想说这个题目真的非常的美丽
首先贪心的想,我想让每个人都不交钱,也就是每个人都是(n - 1)/ 2这个是一个整数的上一个数字的求法:
如果按照这个分,我们就把所有的分完了,数量超过所有的金牌数量了,那就直接输出
如果没有超过说有的金牌数量,那我们看看这种方式用了多少金牌,用了多少金牌就是输出多少
#include<iostream>
#define int long long
using namespace std;
signed main(){
int T,n,k,g,nmax;
cin>>T;
while(T--){
cin>>n>>k>>g;
nmax=n*((g-1)/2);
// if(nmax>=k*g){
// cout<<k*g<<'\n';
// }else{//147 49 49 49 // 163 + 47
// cout<<nmax/g*g<<'\n';
// }
int ans = min(k,nmax / g);
cout << ans * g << '\n';
}
return 0;
}