Codeforces Round #808 (Div. 2) C【二分】【贪心】【反向贪心】

 一开始自己想贪心思路的时候想错了,导致了错误的结果。

这是我想的错误贪心思路,留在这里做个纪念吧, 写的时间还不少,

还认真去调了点bug

 

/*
基础题意:
该人有一个智商q,
然后有n个测试,每个测试都有一个难度ai
编号为i的测试只能再第i天被测。

如果当前这个人遇到一个测试,但是这个测试的难度系数 >他的智商q,
那么智商q就要-1 

我们希望能够通过的测试越多越好

最后输出的字符串中: 
"1"表示该测试通过
"0"表示该测试没有通过 


*/

/*
我的贪心思路是:
1.判断要不要挑战比自己智商高的? 
如果我给自己的智商-1,如果在智商 -1的 情况下,
比自己的(智商低并且挑战日程还在后面的)的挑战数量不变 
那我挑战比自己智商高的就无所谓了 ,

反之,如果-1后,后面比自己智商低的反而挑战不了,那就抵消了,
没什么挑战的意义


2.判断要不要挑战比自己智商低的?
当前日子到了,遇上了就挑战呗,又不损失什么。 
*/


/*
one case:
1
10 10
11 1 9 5 10 3 10 10 1 7

ans:
1111111111 
myans:
0111111111


这个完全推翻了我的做法的可行性 ,
所以我的想法是错误的! 
*/


#include <bits/stdc++.h>
using namespace std;
const int N=1e5+10;
typedef long long LL;
typedef pair<LL,LL> PLL;
LL a[N];

set<PLL> s;



int main()
{
	int t;
	cin>>t;
	while(t--)
	{
		int n;
		LL q;
		cin>>n>>q;
		s.clear();
	
		//智商为q 
		for(int i=1;i<=n;i++)
		{
			cin>>a[i];  //各测试的难度系数为 a[i] 
			
			s.insert({a[i],i});
	    }
	 
	    string res;
	    char o='1',z='0';
	    for(int i=1;i<=n;i++)
	    {
	        s.erase({a[i],i});  //i也表示日期,到期了无论怎样都要删除,方便动态维护 
	    	if(q<=0) break;
	    	if(a[i]<=q)
			{
				res+=o; 
			}
			else if(a[i]>q)  //考虑要不要挑战这种智商比自己高的 
			{
				LL qq=q-1;
				auto it=s.lower_bound({q,0});
				auto jt=s.lower_bound({qq,0});
			
				
				if((*it).first==q&&it!=s.end())
				{
					res+=z;
				}
				else
				{
					res+=o;
			    	q--;
				}
				
			}
		}
		
		
		int l=res.size();
		for(int t=1;t<=n-l;t++)
		{
			res+=z;
		}
		
	   cout<<res.size()<<endl; 
	    cout<<res<<endl;
	
   }
	return 0;
}

正确做法一: 反向贪心 【把减少变成增加】

思路一:
显然,对于降智比赛,如果我们前面参加的多了那么后面的正常比赛也会变为降智比赛。所以我们的最优策略应该是当最后一天时,我们的智商值恰好为1。(参加完这天的降智比赛即为0)
所以我们,可以翻转数组,倒着模拟这种情况,从智商值为0开始,如果遇到比当前智商值大的比赛,就参加,并且智商值加一,当智商值等于Q QQ时我们便不再参加。
————————————————
版权声明:本文为CSDN博主「吃一口AC摇摇乐」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/lucifer1214/article/details/125855257

 

/*
做法一: 
*/
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int a[N];
int ans[N];
int main()
{
	int t;
	cin>>t;
	while(t--)
	{
		memset(ans,0,sizeof ans);
		int n,q;
		cin>>n>>q;
		for(int i=1;i<=n;i++)
		cin>>a[i];
		
		int qq=0;  //当前智商为0 
		for(int i=n;i>=1;i--)
		{
			if(a[i]>qq)
			{
				if(qq<q)
				{
					qq++;
					ans[i]=1;
				}
			}
			else if(a[i]<=qq)
			{
				ans[i]=1;
			}
		}
		
		
		for(int i=1;i<=n;i++)
		{
			cout<<ans[i];
		}
		
		cout<<endl;
	}
	
	return 0;
} 

正确做法二:二分答案法

我们用二分法枚举答案中的1。

记住了,这里遇到之前自己常错的一个点:不能恰好等于!!!

/*
做法二:二分贪心法 

题目答案虽然是用 1和0表示的,但是也能表现出有多少个1
(所以我们要找最多有多少1) 
这个“最多”可以用二分法枚举 (就是找最大的) 
*/
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int a[N],s[N];
int n,q;

bool check(int num)  //整个序列中1的数量是num 
{

	int skip=n-num;	//序列中0的数量也就是能跳过(可以不做)的任务数量
	int tq=q;  
	for(int i=0;i<n;i++)
	{
		if(tq<=0)  //智商值为0了,做不了了 
		{
			s[i]=0;
			continue; 
		}
		
		
		if(a[i]<=tq)  //小于自己的智商,干就完了
		{
			s[i]=1; 
			continue;
		} 
		else if(a[i]>tq)  //大于自己的智商,看看能不能跳过(skip>0) 
		{
			if(skip>0) //如果属于跳过的,也就是可以跳过 
			{
				s[i]=0;
				skip--;
			}
			else if(skip<=0)//否则就是属于要处理的"1" 
			{
				if(tq>0)
				{
				s[i]=1;
				tq--; 
			    }
			}
		} 
	}
	
	
	int co=0;  //看看最终的序列中1的个数是否=num
	for(int i=0;i<n;i++)
	{
		if(s[i]==1)
		{
			co++;
		}
	} 
	
	return co>=num;  //我一开始写的恰好等于,也就是return co==num,WA了
	//之前也遇到过类似的错误,不能恰好等于 
} 

void solve()
{
	cin>>n>>q;
	for(int i=0;i<n;i++)
	scanf("%d",&a[i]);  
	
	if(q>=n) //如果q本身就大于等于n个的n
	{
		for(int i=0;i<n;i++)
		 s[i]=1;
	} 
	else
	{
		int l=0,r=n;
		while(l<r)
		{
			int mid=(l+r+1)>>1;
			if(check(mid)) l=mid;  //往右找最大的 
			else r=mid-1; 
		}
		
		
		//最终l是最终的结果
		//cout<<"l is "<<l<<endl;
		check(l);
	 
	}
		for(int i=0;i<n;i++)
		cout<<s[i];
		cout<<endl;
}

int main()
{
	int t;
	cin>>t;
	while(t--)
	{
		solve();
	}
	
	return 0;
}

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值