习题训练计划 2020 05 26
A题:看到题目把所有的数两两配对,以为n一定是偶数,WA了。
题目没有明确说明n是偶数,因此n也可能为奇数。
思路就是先把数组排一遍序,然后从中间开始往两边输出数组中的数。由于n可以为奇数,所以循环条件不是i>=1&&j<=n,而是i>=1||j<=n。
代码如下:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int a[100000+8];
int main()
{
int t,i,j,k,m,n;
cin>>t;
while(t--)
{
cin>>n;
for(i=1;i<=n;i++)
{
cin>>a[i];
}
sort(a+1,a+n+1);
for(i=n/2,j=n/2+1;i>=1||j<=n;i--,j++)
{
if(j<=n)
{
cout<<a[j]<<' ';
}
if(i>=1)
{
cout<<a[i]<<' ';
}
}
cout<<endl;
}
return 0;
}
B题:WA了6次。下面介绍WA的原因
WA的思路:找到第一个下降点,然后找下降点后面的最小值,求出下降点和最小值的差,然后计算2^0+2^1+…+2^k,直到和>=这个差,输出k+1.
正确思路:找到所有下降点与其之后的最小值的差,再找出所有差中的最大值,然后计算2^0+2^1+…+2^k,直到和>=这个差,输出k+1.
错误的原因就是没有考虑有多个下降点的情况,比如:1 7 5 9 3,
代码如下:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int a[100000+8];
ll quickmi(int a,int b)
{
ll ans=1,base=a;
while(b!=0)
{
if(b&1==1)
{
ans*=base;
}
base*=base;
b>>=1;
}
return ans;
}
int main()
{
int t,n,i,j,k;
cin>>t;
while(t--)
{
cin>>n;
k=0;
int flag=0;
for(i=1;i<=n;i++)
{
cin>>a[i];
}
int p=0;
int ma=a[1];
for(i=2;i<=n;i++)
{
ma=max(ma,a[i]);
if(a[i]<ma)
{
p=max(p,ma-a[i]);
}
}
if(p==0)
{
cout<<"0"<<endl;
continue;
}
ll sum=0;
j=0;
while(sum<p)
{
sum+=quickmi(2,j);
j++;
//cout<<"sum=="<<sum<<endl;
//cout<<"j=="<<j<<endl;
}
/* for(j=1;;j++)
{
if(j==1)
{
base=1;
}
else
{
base*=2;
}
sum+=base;
if(sum>=p)
{
break;
}
}
*/
cout<<j<<endl;
}
return 0;
}
C题:
思路:先把所有的数从大到小排序,然后从头开始sum+a[i],判断sum/i是否小于x,如果小于,直接break输出i-1,代码如下:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int a[100000+8];
bool cmp(int i,int j)
{
return i>j;
}
int main()
{
int t,n,i,j,k,x;
cin>>t;
while(t--)
{
cin>>n>>x;
int flag=0;
for(i=1;i<=n;i++)
{
cin>>a[i];
}
sort(a+1,a+n+1,cmp);
ll sum=0;
int k=0;
for(i=1;i<=n;i++)
{
sum+=a[i];
if(sum/i<x)
{
break;
}
}
cout<<i-1<<endl;
}
return 0;
}
D题:
WA了两次。
WA的思路:先找出a[i]最小的点,然后打爆他,之后用子弹补伤害。事实证明我想的太简单了,反例:7 15
2 14
3 1
正确思路:先判断每一个怪物在遭受上一个怪物的爆炸伤害后的血量,如果>0,就先用枪把这个怪物的血量打到和上一个怪物造成的爆炸伤害相同为止,最后找出此时血量最低的怪物,引爆他,便能形成一个连环爆炸,并且使每一个怪物死亡。
另外一定要注意输入要用scanf,而不是cin,
正确代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll a[300000+8],b[300000+8];
bool cmp(int i,int j)
{
return i>j;
}
int main()
{
int n,t,i,j,k,m;
cin>>t;
while(t--)
{
scanf("%d",&n);
ll ans=0;
for(i=1; i<=n; i++)
{
scanf("%lld%lld",&a[i],&b[i]);
}
if(a[1]<=b[n])
{
;
}
else
{
ans+=a[1]-b[n];
a[1]=b[n];
}
for(i=2; i<=n; i++)
{
if(a[i]<=b[i-1])
{
;
}
else
{
ans+=a[i]-b[i-1];
a[i]=b[i-1];
}
}
ll mi=a[1];
for(i=1;i<=n;i++)
{
if(a[i]<mi)
{
mi=a[i];
}
}
ans+=mi;
cout<<ans<<endl;
}
return 0;
}
E题:
思路:从头开始记录i之前数组a中是否出现过1、-1,然后比较a[i]和b[i]是否相等,如果不相等,那么比较a[i]是否大于b[i],如果大于,我们需要让此时的a[i]不断减去-1以达到==b[i]的目的,也就是说,如果在i之前没有出现过-1,就直接输出NO;如果a[i]小于b[i],我们需要让a[i]不断+1来达到目的,所以在i之前必须出现1,如果没有出现直接输出NO。
最后如果满足上述条件,输出YES即可。
WA了一次是因为我为了提高效率把对数组a和数组b的比较直接放在了数组b输入的循环中,然后使用cin.clear(),cin.sync()清除缓存来避免上一个样例的影响。没有通过应该是VJ的检测系统的问题,具体不明。
代码如下:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int a[300000+8],b[300000+8];
bool cmp(int i,int j)
{
return i>j;
}
int main()
{
int n,i,j,k,t,m;
cin>>t;
while(t--)
{
//cin.clear();
//cin.sync();
scanf("%d",&n);
for(i=1; i<=n; i++)
{
scanf("%d",&a[i]);
}
int flag1=0,flag=0;
for(i=1; i<=n; i++)
{
scanf("%d",&b[i]);
}
for(i=1; i<=n; i++)
{
if(a[i]!=b[i])
{
if(a[i]<b[i])
{
if(flag1==0)
{
cout<<"NO"<<endl;
break;
}
}
else if(a[i]>b[i])
{
if(flag==0)
{
cout<<"NO"<<endl;
break;
}
}
}
if(a[i]==1)
{
flag1=1;
}
if(a[i]==-1)
{
flag=1;
}
}
if(i==n+1)
{
cout<<"YES"<<endl;
}
}
return 0;
}
F题:
前缀和。
用k记录与此时的前缀和相等的最近的上一次前缀和的位置,k的初始值为-1,那么此时的位置-k-1便是以此时的位置为右边界点的合法字串的个数。
正确代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll a[300000+8],b[300000+8];
bool cmp(int i,int j)
{
return i>j;
}
int main()
{
ll n,i,j;
map<ll,ll>h;
map<ll,ll>::iterator p;
cin>>n;
ll sum=0;
ll ans=0;
h[0]=0;
ll k=-1;
for(i=1;i<=n;i++)
{
cin>>a[i];
sum+=a[i];
p=h.find(sum);
if(p!=h.end())
{
k=max(k,h[sum]);
}
ans+=i-k-1;
h[sum]=i;
}
cout<<ans<<endl;
return 0;
}