A. Beat The Odds
题意:给一个数组,删除尽量少的数,使得数组相邻两个数组之和为偶数,输出删除数字的最小数量。
思路:保证让数组里全是偶数或者奇数即可,所以输出原数组偶数和奇数数量少的那个。
代码实现:
#include<bits/stdc++.h>
using namespace std;
int main()
{
int t;
cin>>t;
while(t--)
{
int n;
int e=0,o=0;//记录偶数和奇数
cin>>n;
for(int i=1;i<=n;i++)
{
int x;
cin>>x;
if(x%2==0)
e++;
else
o++;
}
cout<<min(o,e)<<'\n';
}
return 0;
}
B. Shoe Shuffling
题意:一个教室有n个学生,n个学生想互相换鞋穿,每个学生只能穿尺码大于等于他的脚的鞋,问是否有可能能让所有人换到鞋,如果可以,输出第i为学生获得了哪位学生的鞋,如果有多种可能,输出任意一种。
思路:实际上所有同学都只能和尺码相同的同学换,不然大尺码的人没得换了,所以可以记录每一个尺码的鞋对应人的下标,让同一尺码的人互换鞋子,而一个鞋的尺码可以达到1e9,所以可以用map进行映射,如果一个尺码只有一个人,那么这个人肯定不能换鞋子,输出-1。
(实际上不需要这么做,我做题的时候没有看到s[i]<=s[i+1])
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=2e5+10;
map<ll,ll>mp;//因为输入最大值可以到1e9,所以用map进行一个映射关系
vector<ll>vv[maxn];//对于每一个值对应的下标
ll a[maxn];
int main()
{
int t;
cin>>t;
while(t--)
{
mp.clear();
int n;
cin>>n;
int s=0;
for(int i=1;i<=n;i++)
{
int x;
cin>>x;
if(mp.find(x)==mp.end())//如果没有相关映射关系,就建立一个
mp[x]=++s;
vv[mp[x]].push_back(i);
}
bool f=1;
for(int i=1;i<=s;i++)
if(vv[i].size()==1)//如果该尺码只有一个人,那么那个同学没得换了
{
f=0;
break;
}
if(f)
{
for(int i=1;i<=maxn;i++)
{
if(vv[i].size()==0)
break;
//对于每个尺码,第二个人穿第一个人的鞋,第三个人穿第二个人的鞋,第一个人穿最后一个人的鞋
int pre=vv[i][0];
for(int j=1;j<vv[i].size();j++)
{
a[vv[i][j]]=pre;
pre=vv[i][j];
}
a[vv[i][0]]=vv[i][vv[i].size()-1];
}
for(int i=1;i<=n;i++)
if(i==1)
cout<<a[i];
else
cout<<' '<<a[i];
cout<<'\n';
}
else
cout<<-1<<'\n';
for(int i=1;i<=s;i++)
vv[i].clear();
}
return 0;
}
C. Sum of Substrings
题意:给一个二进制数的字符串,给最多k次操作,每次操作可以交换相邻两个字符,求出所有相邻的两个数字组成的十进制数的最小和。
思路:一个所有相邻数字的和可以表示为10*s[1]+11*s[2].....+1*s[n]。从中可以看出只有将1移到开头或者结尾才会改变值。
如果末尾为0,那么尝试将最近的1移到该处,然后如果开头为0,尝试将最近的1移到开头.
代码实现:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=2e5+10;
int main()
{
int t;
cin>>t;
while(t--)
{
string a;
int n,k;
cin>>n>>k;
cin>>a;
int f=0;
if(a[a.size()-1]=='0')//如果末尾为0
for(int i=a.size()-1;i>=0;i--)
if(a[i]=='1')//最近的1
{
if(k>=a.size()-i-1)
{
a[i]='0';
a[a.size()-1]='1';
k-=a.size()-i-1;//操作数减少
}
break;
}
if(a[0]=='0')//如果开头为0
for(int i=0;i<a.size()-1;i++)//末尾不需要访问
{
if(a[i]=='1')//最近的1
{
if(k>=i)
{
a[i]='0';
a[0]='1';
}
break;
}
}
int sum=0;
for(int i=1;i<a.size()-1;i++)
{
sum+=(a[i]-'0')*11;
}
sum+=(a[0]-'0')*10;
sum+=a[a.size()-1]-'0';
cout<<sum<<'\n';
}
return 0;
}
D. Max GEQ Sum
题意:给一个数组,判断是否对于任意的i,j,(i<j),区间[i,j]中的最大值总是大于等于区间和。可以就输出YES,不行就输出NO。
思路:设对于所有的ai,都有一个区间(l,r)使得ai在其中是最大值,输出NO的情况为存在a[l]+a[l+1]+....a[r-1]+a[r]>a[i]。两边同时减去a[i]得a[l]+..a[i-1]+a[i+1]+..a[r]>0,当[l,r]区间最大时,a[l-1]和a[r+1]应该比a[i]大,在这个[l,r]区间中,寻找[L,i-1]的最大值和[i+1,R]的最大值,两者其中一个如果大于0(因为对于任意的区间,所以另外一边如果是负数可以不取),那么就输出NO。
(L,R这里表示为某一区间的边界)
所以先去找每个数前面第一个比他大的下标和后面第一个比他大的下标,这个可以用单调栈找,以获取ai为最大值的最大的区间,区间和可以用前后缀和计算,所以a[i+1]以后的区间可以用前缀和suf[R]-suf[i]计算,a[i-1]以前的区间和可以用后缀和a[L]-a[i-1]计算,寻找区间最大值可以通过线段树维护,因为a[i-1]和a[i+1]是定值,所以维护前后缀和即可。
代码实现:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=2e5+10;
const ll inf=0x3f3f3f3f3f3f3f3f;
ll a[maxn];//记录数组
ll pre[maxn],suf[maxn];//记录前缀和和后缀和
struct node{//线段树
int l,r;
ll mx;
}pretree[maxn<<2],suftree[maxn<<2];
int pregre[maxn];//记录ai前面第一个比ai大的下标
int nxgre[maxn];//记录ai后面第一个比ai大的下标
ll stk[maxn];//模拟栈
void build(int l,int r,int p)//两树一起建
{
pretree[p].l=l;
pretree[p].r=r;
suftree[p].l=l;
suftree[p].r=r;
int mid=(l+r)/2;
if(l==r)
{
pretree[p].mx=pre[r];
suftree[p].mx=suf[r];
return;
}
build(l,mid,p<<1);
build(mid+1,r,p<<1|1);
pretree[p].mx=max(pretree[p<<1].mx,pretree[p<<1|1].mx);
suftree[p].mx=max(suftree[p<<1].mx,suftree[p<<1|1].mx);
}
ll prequery(int ql,int qr,int p)//求前缀和区间最大值
{
int l=pretree[p].l,r=pretree[p].r;
int mid=(l+r)/2;
if(ql<=l&&qr>=r)//如果当前区间完全包括在要查询的区间中,直接返回信息
return pretree[p].mx;
ll ans=-inf;
if(ql<=mid)//如果查询区间和左儿子有交集,就访问左儿子
ans=max(ans,prequery(ql,qr,p<<1));
if(qr>mid)//如果查询区间和右儿子有交集,就访问右儿子
ans=max(ans,prequery(ql,qr,p<<1|1));
return ans;
}
ll sufquery(int ql,int qr,int p)//求后缀和区间最大值
{
int l=suftree[p].l,r=suftree[p].r;
int mid=(l+r)/2;
if(ql<=l&&qr>=r)//如果当前区间完全包括在要查询的区间中,直接返回信息
return suftree[p].mx;
ll ans=-inf;
if(ql<=mid)//如果查询区间和左儿子有交集,就访问左儿子
ans=max(ans,sufquery(ql,qr,p<<1));
if(qr>mid)//如果查询区间和右儿子有交集,就访问右儿子
ans=max(ans,sufquery(ql,qr,p<<1|1));
return ans;
}
int main()
{
int t;
cin>>t;
while(t--)
{
int n;
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
//单调栈
int top=-1;
for(int i=1;i<=n;i++)//寻找后面第一个比ai大的下标
{
while(top!=-1&&a[stk[top]]<a[i])
{
nxgre[stk[top]]=i;
top--;
}
stk[++top]=i;
}
while(top!=-1)//清空栈
{
nxgre[stk[top]]=n+1;
top--;
}
//单调栈
for(int i=n;i>=1;i--)//寻找前面第一个比ai大的下标
{
while(top!=-1&&a[stk[top]]<a[i])
{
pregre[stk[top]]=i;
top--;
}
stk[++top]=i;
}
while(top!=-1)
{
pregre[stk[top]]=0;
top--;
}
//记录前缀和和后缀和
for(int i=1;i<=n;i++)
pre[i]=a[i]+pre[i-1];
for(int i=n;i>=1;i--)
suf[i]=suf[i+1]+a[i];
//建树
build(1,n,1);
bool f=1;
for(int i=1;i<=n;i++)
{
int l=pregre[i];
int r=nxgre[i];
ll rmax=prequery(i+1,r-1,1)-pre[i];//求得i左边最大的区间和
ll lmax=sufquery(l+1,i-1,1)-suf[i];//求得i右边最大的区间和
if(max(lmax,rmax)>0)//如果两者其一大于0,那么就输出NO
{
f=0;
break;
}
}
if(f)
cout<<"YES"<<'\n';
else
cout<<"NO"<<'\n';
}
return 0;
}