目录
A-中位数(贪心)
题目链接
题意
思路
其实就是一串数字,删去一个加到另一个上,求中位数,这里的中位数如果总数是偶数的话那么就去中间靠右的,实际上按照题目的式子即可。
很容易能用贪心得到每一次最优的方案就是让次大的加到最大的上,最大的一直堆,那么经过k次就减少k个数,这里特判一下如果只有一个的话,那么就输出总数。
代码
inline void Case_Test()
{
cin>>n>>k;
sum=0;
for (int i=1;i<=n;i++)
{
cin>>a[i];
sum+=a[i];
}
sort(a+1,a+1+n);
if (n-k==1) cout<<sum<<endl;
else cout<<a[ (n-k+1)/2 ]<<endl;
}
B-k小数查询(尺取法)
题目链接
B-k小数查询_牛客练习赛87 (nowcoder.com)
题意
思路
我一开始看感觉是单调队列,一个队列维护满足条件的情况,但是我还是太年轻了,这样完全行不通,不能得到所有满足条件的区间个数。
后面就用尺取法,在博客里之前也遇到一个尺取法。
尺取法,一种神奇的技巧。在Codeforces中显示它的算法名称叫做"two pointers", 直译成中文的话叫双指针法。 顾名思义,像尺子一样取一段,尺取法通常是对数组保存一对下标,即所选取的区间的左右端点,然后根据实际情况不断地推进区间左右端点以得出答案。
上次那个题给了我这个题灵感,如果遇到一个满足条件的,求个数,实际上就会有两个指针在这个区间上往左往右,那么个数就是,那么在这里题里面首先可以找到x所在的pos位置,然后就对每一个a[i]<x的情况下分隔开,如果满足条件的话我们就加上要求的。
这里我一开始是分成左右两个区间,也就是小于pos的满足的,还有大于pos满足的,这里值得反思反思。实际上我可以pos左边的贡献a个小于x的点,pos右边贡献b个小于x的点,所以我们需要每次移动,,当不满足条件的时候就不必要进行了,后面更不会满足条件了。
再处理point数组(也就是存多少个),这个多少个是这么理解的,在pos左边的就看它左边还有多少个满足条件的,如果是在pos右边的那就看它右边有多少个满足条件的,因为我们的suml和sumr就是这样处理的。
左边是当前a[i]<x的点向左的,因为用尺取法的时候是看它左边还有多少个满足条件的,反之则反。
代码
inline void Case_Test()
{
cin>>n>>x>>k;
for (int i=1;i<=n;i++)
{
cin>>a[i];
if (a[i]==x) pos=i;//pos记录x所在位置
}
num=0;
cnt=0;
for (int i=1;i<pos;i++)
{
if (a[i]<x) point[++num]=cnt,cnt=0;
else cnt++;
//左边是当前a[i]<x的点向左的
//因为用尺取法的时候是看它左边还有多少个满足条件的
}
p=++num;
cnt=0;
for (int i=pos+1;i<=n;i++)
{
if (a[i]<x) point[num++]=cnt,cnt=0;
else cnt++;
//同理,右边的点是看它右边还有多少个
//但是统计的时候已经过了这个点,所以我选择先存数值再++
}
point[num]=cnt;
int l=max(p-k+1,(int)1),r=min(num,l+k-1);
//这里是看是否越界,先确定l,再确定r
while (r-l+1==k&&r<=num&&r>=p&&l<=p)
{
if (r==p)
{
cnt=0;
for (int i=pos+1;i<=n;i++)
if (a[i]>x) cnt++;
else break;
ans+=(cnt+1)*(point[l]+1);
}
else if (l==p)
{
cnt=0;
for (int i=pos-1;i>=1;i--)
if (a[i]>x) cnt++;
else break;
ans+=(cnt+1)*(point[r]+1);
}
//因为point[p]这个pos处的特殊点没有处理,所以在这里暴力一下
else
{
ans+=(point[l]+1)*(point[r]+1);
}
l++;r++;
//移动区间
}
cout<<ans;
}
C-牛老板(记忆化搜索+贪心)
题目链接
题意
思路
这个题是后面补的,我也想过记忆化搜索,但是没想到这个贪心的,我想到的是6出一点,但不是最多,然后9出一点,也不是最多,这样加起来正好是n。但是仔细一想,还是得贪心找当前最大的6的次方,或者当前最大的9的次方,然后去取min,这样贪心是最优的,假如有一个不选,去选一个,那么剩下了5个;同理,也不选,去选,也空出了8个,那么这两个空出来的不是一个系数能够解决的,所以还得是取最大的。
然后就是用map用记忆化搜索了。
代码
unordered_map<int,int>mp;
inline void init()
{
for (int i=1;i<=N;i*=6)
m6[++cnt1]=i;
m6[++cnt1]=inf;
for (int i=1;i<=N;i*=9)
m9[++cnt2]=i;
}
inline int dfs(int n)
{
if (n<6) return n;
if (mp[n]) return mp[n];
int t1=upper_bound(m6+1,m6+1+cnt1,n)-m6;
int t2=upper_bound(m9+1,m9+1+cnt2,n)-m9;
return mp[n]=min(dfs(n-m6[t1-1]),dfs(n-m9[t2-1]))+1;
}
inline void Case_Test()
{
cin>>x;
cout<<dfs(x)<<endl;
}
收获
C题中用unordered_map<int,int>mp 108ms
用map<int,int>mp 272ms