Codeforces1154 总结

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;
} 

猜你喜欢

转载自blog.csdn.net/qq_40423146/article/details/89414646
今日推荐