A. Required Remainder
数学思维题
题意:求0~n内对x取模以后为y的最大数k为多少,就是求n以内最多能够包含几个x(设有m个),即n>=mx+y,移项可得m<=(n-y)/x,则易构造所求最大值k为:k=mx+y=x*((n-y)/x)+y,则ac代码如下:
#include<iostream>
using namespace std;
int main()
{
int x,y,n,t;
cin>>t;
while(t--){
cin>>x>>y>>n;
cout<<x*((n-y)/x)+y<<endl;
}
return 0;
}
B. Multiply by 2, divide by 6
数学思维题
题意:给出一个数n,求通过几次除6,乘2的操作可以得到1,输出操作数,如果得不到1,就输出-1
#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
int n,t;
cin>>t;
while(t--){
cin>>n;
int cnt=0;
while(n%3==0){
if(n%6==0) n/=6;
else n*=2;
cnt++;
}
if(n==1)cout<<cnt<<endl;
else cout<<"-1"<<endl;
}
return 0;
}
C. Move Brackets
简单栈应用
#include<iostream>
#include<stack>
using namespace std;
int main()
{
int n,t;
cin>>t;
while(t--){
stack<char> st;
cin>>n;
char s;
for(int i=0;i<n;i++){//先把给出序列中的(全部和)匹配完,剩下栈中都是(,其数量即最小移动步数
cin>>s;
if(s=='('){
st.push(s);
}else if(!st.empty()&&s==')'){
st.pop();
}
}
cout<<st.size()<<endl;
}
return 0;
}
D. Zero Remainder Array
题意:给出一个数组a,开始有x=0,x可以进行自增,次数不限,对数组的每个元素,最多只能进行一次ai+x操作,问使得数组每个元素都能整除k的最小操作数
参考博文地址
解题思路:对数组所有元素求出其达到整除k时所需要的步数为k-a[i]%k,当同余元素有多个时,会进行循环,从同余元素中的最小值开始,变成k的倍数,比如k为3时数组中有两个1,两个1到达3所需步数都为2,先让第一个1到达3,此时第二个1则应达到3的倍数6,据此推演,出现多个同余元素(假设有maxn个)时则除同余元素中最小值以外的元素所需步数应是加上(maxn-1)*k,而最小值所需步数为其本身+1(x是从0开始自增的,这里需要加1),将次数最多的最大同余数组所需操作数看作最后所需最小操作数,而前面更小的元素看成x达到同余数组第一个最小值时所需步数的子步(所以这里用了map数组进行排序)
注:我开始用的int,wa在了test5,后来改成longlong就ac了
#include<iostream>
#include<map>
using namespace std;
typedef long long ll;
int main()
{
ll n,k,t,x;
cin>>t;
while(t--){
map<ll,ll> mp;//以到达k的倍数所需步数为关键字,同余元素个数为值
mp.clear();
cin>>n>>k;
ll maxn=0,maxs=0;//maxn存储最大次数,maxs存储同余元素最大次数到达nk所需最小步数
for(int i=1;i<=n;i++){
cin>>x;
x%=k;
if(x==0)continue;//忽略已经能够整除k的元素的情况
x=k-x;
mp[x]++;
if(mp[x]>maxn){maxs=x;maxn=mp[x];}//更新
else if(maxn==mp[x]&&x>maxs)maxs=x; //次数相同时取所需步数大的,使它尽可能经过更多的中间子步
}
if(maxs==0)cout<<"0"<<endl;
else cout<<maxs+1+(maxn-1)*k<<endl;
}
return 0;
}
E1. Reading Books (easy version)
解题思路:使得ab同时为1加上a为1或b为1时的时间总和T最小即所求答案
ac代码如下:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=2*1e5+3;
int n,t[maxn],ta[maxn],tb[maxn];
int main()
{
int k;
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++){t[i]=ta[i]=tb[i]=0;}
int tt,a,b,T=0,p1=0,p2=0,p3=0;
for(int i=0;i<n;i++){
scanf("%d%d%d",&tt,&a,&b);
if(a&&b){t[p1++]=tt;}
else if(a){ta[p2++]=tt;}
else if(b){tb[p3++]=tt;}
}//将ab同时为1的time存入t数组,只有a为1时存入ta,只有b为1时存入tb,方便分开处理并排序
if(p1+p2<k||p1+p3<k){printf("-1\n");return 0;}//如果两个人中有一个达不到k就直接退出打印-1
sort(t,t+p1);
sort(ta,ta+p2);
sort(tb,tb+p3);
tt=a=b=0;
for(int i=0;i<k;i++){
if(tt<p1&&a<p2&&b<p3){
if(t[tt]<ta[a]+tb[b])T+=t[tt++];
else T+=ta[a++]+tb[b++];
}else if(tt==p1){
T+=ta[a++]+tb[b++];//没有ab同时为1时的time就直接加上ab单独为1时数值
}else{T+=t[tt++];}//ab单独为1的情况处理完了还不够k继续处理同时为1的情况
}
printf("%d\n",T);
return 0;
}
参考博文
这道题我还有疑问,开始我的思路是直接给t数组排序,让a和b数组跟着t数组动,以免ab和t对应错误,我用了归并排序,过程中让ab一起动,后来甚至用上了冒泡排序,但是始终不行,ab数组和t数组老是对应错误导致结果错误,后来看了上面这位博主的文章改用三个数组来存,的确是避免了我一个数组存时间来排序时ab数组的对应问题,但是按道理来说我开始这个思路应该可以的呀,怎么会错呢?请各位大佬指教,谢谢了
错误代码如下:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=2*1e5+3;
int n,a[maxn],b[maxn],t[maxn],ta[maxn];
int cnt=0;
//处理方法1 归并排序
void merge(int l,int mid,int r)
{
memset(ta,0,sizeof(ta));
int p1=l,p2=mid+1,pos=l;
while(p1<=mid&&p2<=r){
if(t[p1]<=t[p2])ta[pos++]=t[p1++];
else{
swap(a[p1],a[p2]);swap(b[p1],b[p2]);
ta[pos++]=t[p2++];
}
}//p1=2,p2=5的时候a数组元素交换不成功,怎么回事?
while(p1<=mid)ta[pos++]=t[p1++];
while(p2<=r)ta[pos++]=t[p2++];
for(int i=l;i<=r;i++)t[i]=ta[i];
}
void merge_sort(int l,int r)
{
if(l<r){
int mid=(l+r)/2;
merge_sort(l,mid);
merge_sort(mid+1,r);
merge(l,mid,r);
}
}
/*void Bubble_sort() 处理方法2 冒泡排序
{
for(int i=1;i<=n-1;i++){
for(int j=i+1;j<=n;j++){
if(t[i]>t[j]){
swap(t[i],t[j]);
swap(a[i],a[j]);
swap(b[i],b[j]);
}
}
}
}*/
int main()
{
int k;
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++){t[i]=a[i]=b[i]=0;}
int num1=0,num2=0,T=0,tk=0;
for(int i=1;i<=n;i++){
scanf("%d%d%d",&t[i],&a[i],&b[i]);
if(a[i])num1++;
if(b[i])num2++;
}
if(num1<k||num2<k){printf("-1\n");return 0;}
for(int i=1;i<=n;i++){
if(tk==k)break;
if(a[i]==1&&b[i]==1){T+=t[i];tk++;a[i]=0,b[i]=0;}
}
if(tk<k){
//sort(t+1,t+n+1);不能直接对t数组排序,不然ab数组和t数组对应不上
merge_sort(1,n);
//Bubble_sort();
num1=num2=0;
for(int i=1;i<=n;i++){
if(num1+tk==k&&num2+tk==k)break;
if(num1+tk<k&&a[i]==1){T+=t[i];++num1;}
if(num2+tk<k&&b[i]==1){T+=t[i];++num2;}
}
}
printf("%d\n",T);
return 0;
}
E2. Reading Books (hard version)
题意:这道题算是上面那道题的加强版,给出n本书,从中选m本使得同为1或ab其中一个为1的情况加起来总和尽可能小
解题思路:如果说给出的n本书a和b其中一个的1的个数达不到k个,那么不管怎么选m本书肯定都不符合要求,因此这个时候直接退出打印-1,都能选到k本书时先按上题思路选出尽可能小的k本书时间总和,然后用一个tm存储所有书时间,已经被用过的过程中置0,在选完k本还不够m本的情况下继续从tm中从小开始选使时间总和最小,问题是怎么让