A
a+b+c a+b b+c a+c 求a,b,c
排序后最大值-其他3个
debug时多写一个输出 wa1 不过没算罚时 下次debug一定要注释
B
给一串序列 把它们变成相同的数
可以-d +d 不操作
求最小非负整数 d
先排序复杂度nlogn
暴力O(n)解决的问题 比赛时只会二分O(nlogn)做,n是100,想不到暴力的所有情况,没有在纸上推 暴力思路清晰但需要全面 二分结果一定正确,但思路比较绕,而且还要保证二分的正确性 满足递增
1.暴力
1 4 4 6
样例最多写出3个不同的数 能找到d
可以发现如果数超过3种 找不到共同的d
可以统计种类 set 进set可以vis数组/从i=2与前一个数不同进集合
1种 d=0 无需修改
2种 分奇偶
奇奇 3 5 偶偶 4 6
d为差值1半
奇偶 3 6 只能选择其中之一 -/+差值 d=差值
3种 递增序列 如果差值一样则满足d=ww[1]-ww[0],否则不行-1。
2.二分
二分的话 排序后取1-maxx的中间值,可以说是二分最大值相当于二分差值
mid可以认为从比较平均的值开始
首先如果所有数已经全部相等 直接输出d=0 return
check时 cha=mid-a[0]
如果a[i]==mid 跳过 否则看abs(a[i]-mid) 是否都相同 有不同的return false
都相同循环到最后 返回true
如果check为true,继续看更小mid的是否存在r=mid-1; 即更小的差值是否存在 cha=mid-a[0]
不行将mid变大 l=mid+1 即cha=mid-a[0]变大
找到一个最为合适的ans=mid
输出的结果是ans-a[0]
如果找不到ans=0 输出-1
C
周期的求余处理
1,4,7 鱼
2,6 兔
3,5吃鸡
1周的所有情况
3天鱼 2天兔 2天鸡
30 20 10
先看能走几个完整的星期,减去后剩余的必走不过1周,枚举从某天开始最久即可
ll a,b,c;
cin>>a>>b>>c;
ll ta=a/3,tb=b/2,tc=c/2;
ll tmin=min(min(ta,tb),tc);
ll ans=tmin*7;
a-=3*tmin;
b-=2*tmin;
c-=2*tmin;
ll maxx=0;
for(int i=1;i<=7;i++)
{
ll cnt=0;
ll resa=a,resb=b,resc=c;
for(int j=i;j<i+7;j++)//j=1-7 1 2 3 4 5 6 0
{
if(resa<0 || resb<0 || resc<0)//变为0的某个数由于天数连续 0后不应该- 变为-1 多加一次cnt--
{
cnt--;
break;
}
if(j%7==0 || j%7==1 || j%7==4)
resa--,cnt++;
else if(j%7==2 || j%7==6)
resb--,cnt++;
else if(j%7==3 || j%7==5)
resc--,cnt++;
}
maxx=max(maxx,cnt);
}
cout<<ans+maxx<<endl;
D
阳光下 使用电池 能使蓄电池电量+1
如果蓄电池已满 则在阳光下只能使用蓄电池,使得下次蓄电池未满电荷++
非阳光下尽量使用蓄电池 蓄电池用完才使用电池
5 2 1
0 1 0 1 0
n点 b电池 a蓄电池
0 1有无阳光
int ww[maxn];
int main()
{
IO;
int n,b,a;
cin>>n>>b>>a;
for(int i=1;i<=n;i++)
{
cin>>ww[i];
}
int aa=a,bb=b,cnt=0;
for(int i=1;i<=n;i++)
{
if(ww[i]==1 && bb>0 && aa<a)
{
bb--;
aa++;
cnt++;
}
else if(aa>0)
aa--,cnt++;
else if(bb>0)
bb--,cnt++;
}
cout<<cnt<<endl;
return 0;
}
E 1800
第一个教练先取最大值 再取最大值左边k个右边k个,不够k取全部
第二个教练同理
2 4 5 3 1 11111
集合set的prev()操作和upper_bound()操作,erase()操作
set存位置
因为值范围在1-n之间 n<2e5 b[a[i]]=i 值对应位置
找到最大值位置 pos=n开始找到 b[pos] 开始id=1 vis数组存,顺便访问 vis[b[pos]]=id;删除掉最大值位置b[pos]处的值
upper_bound()先删除左边的,位置始终由第一个大于最大值位置确定
同理t=k;删除右边 只要不越界 po==s.end()
int a[maxn],b[maxn],vis[maxn];//找到最大值位置
set<int> s;
int main()
{
IO;
int n,k;
cin>>n>>k;
for(int i=1;i<=n;i++)
{
cin>>a[i];
b[a[i]]=i;//每个值对应位置
s.insert(i);
}
int pos=n,t=0,id=1;
while(!s.empty())
{
while(vis[b[pos]])//未被访问的最大值
{
pos--;
}
vis[b[pos]]=1;
s.erase(b[pos]);
t=k;
while(!s.empty() && t--)
{
auto po=s.upper_bound(b[pos]);
if(po==s.begin())
break;
else
{
po=prev(po);
int it=*po;
s.erase(po);
vis[it]=id;
}
}
t=k;
while(!s.empty() && t--)
{
auto po=s.upper_bound(b[pos]);
if(po==s.end())
break;
else
{
int it=*po;
s.erase(po);
vis[it]=id;
}
}
if(id==1)
id=2;
else
id=1;
}
for(int i=1;i<=n;i++)
cout<<vis[i];
cout<<endl;
return 0;
}
F
n个东西的价格,有m张劵,买k个的最小花费
每张劵能使xi个物品中最便宜的xj个免费
找到相同物品中能够最大限度优惠的劵使用
假如劵 3个免费2个 4个免费1个 res[4]为啥不为2因为 4个免费2个比3个免费2个多花1个的钱不如不用。
i代表当前买多少个,j枚举到当前位置如果有劵 是否使用劵
本来买到当前第j个时 排序保证了买到前j个价格和最小
如果有劵可以使用 使得买了j个后 最便宜的res[j]个被免费掉
花的钱就是后j-res[j]的钱 ,前缀和实现
int n,ans=0;
ll a[maxn],res[maxn];//res代表买x个最大使用劵数
ll sum[maxn],dp[2005];
int main()
{
IO;
ll n,m,k;//m张劵买k个
cin>>n>>m>>k;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
sort(a+1,a+n+1);//排序优先买最便宜的
ll x,y,maxx=0;
while(m--)
{
cin>>x>>y;
res[x]=max(res[x],y);//如果前面免费的更大优先取前面 不会递推到这一次多算1个钱
}
for(int i=1;i<=n;i++)
{
sum[i]=sum[i-1]+a[i];
}
for(int i=1;i<=k;i++)//当前买的i个中 前j个跑优惠
{
dp[i]=sum[i];//排序后 不使用任何劵 前i个最小花费
for(int j=1;j<=i;j++)//是否在当前位置使用优惠劵 不优惠时保证买的前j个价格和最小
{
dp[i]=min(dp[i],dp[i-j]+sum[i]-sum[i-(j-res[j])]);//i-j个完全没有优惠 +花了j-res[j]的钱
}
}
cout<<dp[k]<<endl;
return 0;
}