一开始自己想贪心思路的时候想错了,导致了错误的结果。
这是我想的错误贪心思路,留在这里做个纪念吧, 写的时间还不少,
还认真去调了点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;
}