目录
A-Yet Another String Game
题意:
给一个字符串,两个人轮流改一个字符(改为一个与原来不一样的字符,同时改过的不能再改)一个想要结果尽可能大,一个想要结果尽可能的小。求最后的字符串
思路:
简单来说就是连个人从前往后依次改字符,要变小就把当前的字符尽可能改小,由于不能改为一样的,所以当前字符是a就改为b否则改为a,要变大就改大不是z就改为z,是z就改为y。
AC代码
#include <iostream>
#include <cstring>
#include <cmath>
#include <vector>
#include<algorithm>
using namespace std;
typedef long long ll;
int n;
string in;
int main()
{
cin>>n;
while(n--)
{
cin>>in;
for(int a=0;a<in.length();a++)
{
if(a%2==0)
{
if(in[a]=='a')
{
in[a]='b';
}else
{
in[a]='a';
}
}else
{
if(in[a]=='z')in[a]='y';
else in[a]='z';
}
}
cout<<in<<endl;
}
return 0;
}
B-The Great Hero
题意:
告诉你骑士的生命值和怪物的数量以及怪物的生命值和血量,每次攻击都会使怪物和骑士的生命值减去对方的攻击力数值。求最后骑士能不能消灭所有的怪(想起了酒馆战旗)。
思路:
不需要管前面的怪,只要到最后一只怪的时候有没有血以及能不能干掉这只怪就好了,问题就是怎么选择这最后一只怪。两种方法:1、暴力枚举每一只怪作为最后一只怪 2、把攻击力最高的怪作为最后一只。
AC代码
#include <iostream>
#include <cstring>
#include <cmath>
#include <vector>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxx=100005;
ll n,t,ha,hh;
struct node{
ll he,at,va; //对应怪的血量,攻击力,干掉这只怪要消耗多少血
}mons[maxx];
void cu(int i) //计算消耗血量的函数
{
ll ti = mons[i].he%ha==0?mons[i].he/ha:mons[i].he/ha+1;
mons[i].va = mons[i].at*ti;
}
int main()
{
cin>>t;
while(t--)
{
int ind=0;
cin>>ha>>hh>>n;
for(int a=0;a<n;a++)
{
cin>>mons[a].at;
if(mons[ind].at<mons[a].at)ind=a;
}
for(int a=0;a<n;a++)
{
cin>>mons[a].he;
if(a!=ind)
{
cu(a);
hh-=mons[a].va;
}
}
bool win=true;
if(hh<=0)win=false;
else
{
while(hh>=0 && mons[ind].he>=0)
{
hh-=mons[ind].at;
mons[ind].he-=ha;
}
if(mons[ind].he>0)
{
win = false;
}else
{
win = true;
}
}
cout<<(win?"YES":"NO")<<endl;
}
return 0;
}
C-Searching Local Minimum
题意:
给一个长度为n的数组,由1-n组成,你有100次询问的机会,每回可以问一个位置的数字。在次数内找出一个局部最小值,a[i-1]> a[i] <a[i+1] 。其中a0和an+1为无穷大。
思路:
二分当时觉着这种题是二分,但是就是不知道怎么下手,后来看了别人的题解才知道还可以这么玩(建议画个图,这样好理解)。
首先明确一个情况对于 (i<j) a[i-1] > a[i] && a[j]<a[j+1] 时不论a[i]和a[j]的大小如果中间必有局部最小值。
如果a[i]->a[j]为单调递增那么谷点就是a[i] ,如果是单调递减那么谷点就是a[j],不是单调就不说肯定有谷点。
接下来开始二分,mid=(i+j)/2 , 如果 a[i] < a[mid] 那么右边界就是mid,如果 a[i]>a[mid] 那么就看看a[mid-1]的值 ,如果a[mid-1] < a[mid]那么 [i,mid-1]就满足了之前说的情况,如果a[mid-1] > a[mid] 那么[mid,j]就满足了条件。这样不断二分就可以缩小范围。
最后我懒的区掐范围,并且确定100次一定够用所以当r-l<2时我就停下来直接询问中间的每一个找谷点。
AC代码
#include <iostream>
#include <cstring>
#include <cmath>
#include <vector>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxx=100005;
int n,m,ans,k,arr[maxx],cnt=0;
int query(int i)
{
if(arr[i])return arr[i];
int t;
cout<<"? "<<i<<endl;
cnt++;
cin>>t;
return t;
}
int main()
{
cin>>n;
arr[n+1] = arr[0] = 2*maxx;
arr[1] = query(1);
arr[n] = query(n);
int l = 1,r=n,mid=0;
while(l<r && l!=r-1 && l!=r-2)
{
if(arr[l-1]>arr[l] && arr[l]<arr[r])
{
mid = (l+r)/2;
arr[mid] = query(mid);
if(arr[mid]>arr[l])
{
r = mid;
}else
{
arr[mid-1] = query(mid-1);
if(arr[mid-1]<arr[mid])
{
r = mid-1;
}else
{
l = mid;
}
}
}else if(arr[l]>arr[r] && arr[r]<arr[r+1])
{
mid = (l+r)/2;
arr[mid] = query(mid);
if(arr[mid]>arr[r])
{
l = mid;
}else
{
arr[mid-1] = query(mid-1);
if(arr[mid-1]>arr[mid])
{
l = mid;
}else
{
r = mid-1;
}
}
}
}
if(l>1)
arr[l-1] = query(l-1);
if(r<n)
arr[n+1] = query(n+1);
for(int a=l;a<=r;a++)
{
arr[a] = query(a);
}
for(int a=l;a<=r;a++)
{
if(arr[a]<arr[a-1] && arr[a]<arr[a+1])
{
cout<<"! "<<a<<endl;
break;
}
}
return 0;
}
D1-Painting the Array I
题意:
定义一个序列的段数是把相邻的相同数字消去到只剩一个后的序列长度。把一个给你的序列拆成两个,让他们的段数和最大
思路:
贪心,设最后俩个序列是a0和a1,对于a[i],如果他和a0的最后一个一样就把他给a1,如果a0,a[i],a[i+1]三者都不相同就把他给a1,其他情况下都给a0.
AC代码
#include <iostream>
#include <queue>
using namespace std;
typedef long long ll;
int n,m,k,t,arr[100005];
queue<int> in,a0,a1;
int main()
{
cin>>n;
for(int a=1;a<=n;a++)
{
cin>>k;
in.push(k);
}
while(!in.empty())
{
k = in.front();
in.pop();
if(a0.empty())
{
a0.push(k);
continue;
}
if(a0.back()==k)
{
a1.push(k);
continue;
}
if(!in.empty() && a0.back()!=k && in.front()!=k && a0.back()!=in.front() &&(!a1.empty()) && a1.back()!=k) //因为是求最大值所以可以这么处置但是如果求最小值的时候用同样的思路就会出错
{
a1.push(k);
continue;
}
a0.push(k);
}
int last=0,all=0;
while(!a0.empty())
{
if(last!=a0.front())
{
all++;
}
last=a0.front();
a0.pop();
}
last=0;
while(!a1.empty())
{
if(last!=a1.front())
{
all++;
}
last = a1.front();
a1.pop();
}
cout<<all<<endl;
}
D2-Painting the Array II
题意:
定义一个序列的段数是把相邻的相同数字消去到只剩一个后的序列长度。把一个给你的序列拆成两个,让他们的段数和最小。
思路:
如果a[i]和a0或者a1的最后一个一样就放到对应的序列中,否则就看看a0和a1的最后一个数字,看哪一个数字下一次出现最早,把他放到出现迟的那个序列中去(没有再出现就假设为在无穷远出出现就好了)。所以需要一个方式记录每一个数字下一次的出现位置。
AC代码
#include <iostream>
#include <queue>
using namespace std;
typedef long long ll;
const int maxx = 100005;
int n,m,k,t,cnt[maxx],a0last=0,a1last=0;
queue<int> in,a0,a1,tmp[maxx];
void a0push(int k)
{
a0.push(k);
a0last = tmp[k].empty()?maxx+1:tmp[k].front();
tmp[k].pop();
}
void a1push(int k)
{
a1.push(k);
a1last = tmp[k].empty()?maxx+1:tmp[k].front();
tmp[k].pop();
}
int main()
{
cin>>n;
for(int a=1;a<=n;a++)
{
cin>>k;
cnt[k]++;
tmp[k].push(a);
in.push(k);
}
for(int a=0;a<maxx;a++)
{
tmp[a].pop();
}
while(!in.empty())
{
k = in.front();
in.pop();
if(a0.empty() || k == a0.back())
{
a0push(k);
continue;
}
if(a1.empty() || k == a1.back())
{
a1push(k);
continue;
}
if(a0last<a1last)
{
a1push(k);
continue;
}
a0push(k);
}
int last=0,all=0;
while(!a0.empty())
{
if(last!=a0.front())
{
all++;
}
last=a0.front();
a0.pop();
}
last=0;
while(!a1.empty())
{
if(last!=a1.front())
{
all++;
}
last = a1.front();
a1.pop();
}
cout<<all<<endl;
}