感受:一开始被b给唬住了,看着是个博弈,结果越看越像水题,浪费了不少时间,c2没想到 O ( n ) O(n) O(n)做法,勉强打了个暴力交的c1,然后快结束了才看的D,也想到01背包了,没时间了。得出一个教训,每次遇到自己不会的题的时候往下看一道题,说不定就有会做的,可能太往下的题不在知识范围,那就不看。不能在一个题上吊死。。就像这个D最后一共才思考了七八分钟。。要打代码结果结束了。
A题
题目大意:
在a中找一个b的最短子序列。
思路:
都说最短了,就是1呗。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int inf=0x3f3f3f3f;
const int N=1e3+10;
int a[N],b[N];
int main()
{
int t;
cin>>t;
while(t--)
{
map<int,int>f;
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>a[i],f[a[i]]=1;
for(int i=1;i<=m;i++) cin>>b[i];
int flag=0;
for(int i=1;i<=m;i++)
{
if(f[b[i]])
{
flag=1;
cout<<"YES"<<endl;
cout<<1<<' '<<b[i]<<endl;
break;
}
}
if(!flag) cout<<"NO"<<endl;
}
return 0;
}
B题
题目大意:
两个人拿石子,谁最先没石子可以拿谁输。
思路:
小小分析可以发现,谁拿最后一堆谁赢。那么每个人的最优做法就是让自己拿最后一堆。为了让下一步自己先手,所以遇到不是1的石子堆时,可以拿到只剩下1个石子,然后让对方拿,这样还可以保证自己的先手,但如果下一个是1的话,那么当前这一堆就要全拿掉,所以怎么选我们不用管他俩,他们自己精明得很,我们只需算出谁能第一个保证先手,先手的那个一定会很精明的让自己赢。就是算出前面有几个1来就行了。特判一个全是1的情况。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int inf=0x3f3f3f3f;
const int N=1e5+10;
int a[N];
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
int t;
cin>>t;
while(t--)
{
int n,num=0;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
if(a[i]==1) num++;
}
if(n==1)
{
cout<<"First"<<endl;
continue;
}
if(num==n)
{
if(n&1) cout<<"First"<<endl;
else cout<<"Second"<<endl;
continue;
}
int flag=1;
int now=1;
while(a[now]==1&&now<=n)
{
flag^=1;
now++;
}
if(flag) cout<<"First"<<endl;
else cout<<"Second"<<endl;
}
return 0;
}
C题
题目大意:
给你a和b两个01串,你可以执行这个操作,选定a的前缀反转每个字符,然后把这个前缀倒过来。用最多2n次操作把a变成b。
思路:
每次都反转再倒过来太麻烦了,索性直接把a变成单一字符,最多n次操作,然后再把这个单一字符a变成b,最多n次操作。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int inf=0x3f3f3f3f;
const int N=1e5+10;
char a[N],b[N];
int ans[N<<1];
int main()
{
int t;
cin>>t;
while(t--)
{
int n,cnt=0;
cin>>n;
cin>>(a+1);
cin>>(b+1);
for(int i=1;i<n;i++)
{
if(a[i]!=a[i+1])
{
ans[++cnt]=i;
}
}
if(a[n]!='0') ans[++cnt]=n;
int r=n;
int flag=0;
while(r>=1)
{
if(b[r]-'0'==flag) r--;
else
{
ans[++cnt]=r;
flag^=1;
}
}
cout<<cnt<<' ';
for(int i=1;i<=cnt;i++) cout<<ans[i]<<' ';
cout<<endl;
}
return 0;
}
D题
题目大意:
就是两个数组每次取最小的加入到一个序列中,问能否从最后的序列得到原始的两个数组。
思路:
稍微分析一下发现,这一个序列可以分段,从数i开始找直接遇到一个大于它的为止,这算一段,然后把这些段任意组合,能否组合出这两个数组。那么我们就可以把这些段按01背包的方式进行组合,问最后能否有一个容量为n的背包组合出一个长度为n的数组。能的话就是yes,否则no
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int inf=0x3f3f3f3f;
const int N=2e4+10;
int a[N],dp[N],d[N];
int main()
{
int t;
cin>>t;
while(t--)
{
memset(dp,0,sizeof(dp));
int n;
cin>>n;
for(int i=1;i<=2*n;i++) cin>>a[i];
int cnt=0,now=1;
while(now<=2*n)
{
int len=1,in=now;
while(a[in]>a[now+1]&&now<2*n) now++,len++;
d[++cnt]=len;
//cout<<len<<endl;
now++;
}
for(int i=1;i<=cnt;i++)
for(int j=n;j>=d[i];j--)
dp[j]=max(dp[j],dp[j-d[i]]+d[i]);
if(dp[n]==n) cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
return 0;
}