A 题目链接:Problem - A - Codeforces
input:
4
3
1 1 2
3
1 1 1
8
10 9 13 15 3 16 9 13
2
18 9
output:
1 1 2
1 1 1
13 9 13 15 3 9 16 10
9 18
题意:给出n个整数,按一定顺序排列,使得每相邻两个数ai,ai+1,,满足(ai + ai+1)/ 2是整数的数对最多
策略:一道水题,只需先输出所有奇数,再输出所有偶数即可~~
代码如下:
#include<bits/stdc++.h>
using namespace std;
const int N=2005;
int t,n;
int a[2005];
int main()
{
cin>>t;
while(t--)
{
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
for(int i=1;i<=n;i++)
{
if(a[i]%2==1)
cout<<a[i]<<" ";
}
for(int i=1;i<=n;i++)
{
if(a[i]%2==0)
cout<<a[i]<<" ";
}
cout<<endl;
}
return 0;
}
B 题目链接:Problem - B - Codeforces
input:
5
3
TMT
3
MTT
6
TMTMTT
6
TMTTTT
6
TTMMTT
output:
YES
NO
YES
NO
YES
题意:给定一个由T和M组成且长度为n的串,问能否将其拆分成n/3个”TMT“子序列
策略:赛后搜索题解,发现很多大佬提出了括号匹配,现在想想确实很像,我的做法是先用两个前缀和数组,遍历一遍串,分别记录M和T的前缀和,然后保证每到M时,当前M的数量始终不大于T。然后再memset一下,用后缀和反向遍历一次,做法同上。
另外注意加一下特判,一定是T数量是M的2倍时,才有可能成功拆分,否则直接输出NO。
代码如下(略长,属实拉跨了,不如同队俩哥们写的好看)
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int t,n;
int am[N],at[N]; //M和T的前缀和数组
int main()
{
cin>>t;
while(t--)
{
cin>>n;
string s;
cin>>s;
memset(am,0,sizeof am);
memset(at,0,sizeof at);
for(int i=0;i<n;i++) //计算前缀和
{
if(s[i]=='M')
{
am[i]=am[i-1]+1;
at[i]=at[i-1];
}
else
{
am[i]=am[i-1];
at[i]=at[i-1]+1;
}
}
if(am[n-1]*2!=at[n-1]) //特判
{
cout<<"NO"<<endl;
continue;
}
int flag=0;
for(int i=0;i<n;i++) //遍历
{
if(s[i]=='M'&&am[i]>at[i])
flag=1;
else
continue;
}
if(flag==1)
{
cout<<"NO"<<endl;
continue;
}
memset(am,0,sizeof am);
memset(at,0,sizeof at);
for(int i=n-1;i>=0;i--) //后缀和
{
if(s[i]=='M')
{
am[i]=am[i+1]+1;
at[i]=at[i+1];
}
else
{
am[i]=am[i+1];
at[i]=at[i+1]+1;
}
}
flag=0;
for(int i=n-1;i>0;i--) //遍历
{
if(s[i]=='M'&&am[i]>at[i])
flag=1;
else
continue;
}
if(flag==1)
{
cout<<"NO"<<endl;
continue;
}
cout<<"YES"<<endl;
}
return 0;
}
C 题目链接:Problem - C - Codeforces
input1:
3
3 1 2
output1:
3
input2:
1
5
output2:
0
input3:
6
1 6 3 3 6 3
output3:
11
input4:
6
104 943872923 6589 889921234 1000000000 69
output4:
2833800505
题意:有n个人,第i个人的速度为si,di为前i个人中最大速度和最小速度的差值,将这n个人按一定顺序排列,使得d1+d2+……+dn的值最小,输出该最小值
策略:读题后想到了区间dp,于是先套了一个区间dp的板子进去,状态转移方程没仔细研究,按着自己的推测整了一个: dp[i][j] = min(dp[i+1][j]+a[i],dp[i][j-1]+a[j]);发现样例4和答案比较接近,于是推测dp的大方向是对的。
赛时 @与风做友 同学提出了更新区间最大值和最小值的想法,我当即有了思路,既然区间dp最终得到的结果是一定的,并且不要求初始排列的顺序固定,那直接sort一下就ok了,这样可以保证每个区间的最左端是最小值,最右端是最大值。
同时发现了该题的规律:区间中最后一个d一定是该区间最大值和最小值的差,于是得出下面的状态转移方程: dp[i][j] = min(dp[i + 1][j] , dp[i][j - 1]) + a[j] - a[i]
修改完以后,就华丽地AC了~
代码如下:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll; //必开long long
const int N=2005;
ll a[N]; //速度数组
ll dp[N][N]; //dp[i][j]代表[i,j]区间中该问题的最小值
int n;
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
sort(a+1,a+n+1); //快排
for(int r=2;r<=n;r++) //区间长度
{
for(int i=1;i<=n-r+1;i++)
{
int j=i+r-1;
if(r!=2)
dp[i][j]=min(dp[i+1][j],dp[i][j-1])+a[j]-a[i]; //状态转移方程
else
dp[i][j]=a[j]-a[i]; //r==2时特判
}
}
cout<<dp[1][n]<<endl;
return 0;
}
D 题目链接:Problem - D - Codeforces
input:
2
1
00
11
01
3
011001
111010
010001
output:
010
011001010
题意:给定一个数字n,并给出3个长度为2n的01串,要求构造一个长度为3n的01串,使至少两个先前给的串是该串的子序列
策略:赛时搞了好久,最后都WA了,队伍整体思路有些问题,现在给出补题的思路。
三个串各设置一个指针,因为串只有0和1,所以必定会至少有2个串当前的指针对应的值相等。将这个值插入答案串中,相等的串上两个指针右移。重复该过程,直至其中一个串遍历完成。
此时我们可以sort一下三个指针的值,最大的那个值,说明其代表的串全部构造完成,此时我们找到指针值排第二的那个,该串构造度最高,所以直接把后面尚未构造的部分直接插入进答案串中。
然后答案串0~3n-1输出一下即可
代码如下:
#include<bits/stdc++.h>
using namespace std;
const int N=3E5+5; //注意数据量
int t,n;
string a,b,c; //三个串
int s[N]; //答案串
int main()
{
cin>>t;
while(t--)
{
cin>>n;
cin>>a>>b>>c;
int i=0,j=0,k=0,l=0;
while(i<a.length()&&j<b.length()&&k<c.length()) //构造
{
if(a[i]==b[j])
{
s[l]=a[i]-48;
++i;
++j;
++l;
}
else if(b[j]==c[k])
{
s[l]=b[j]-48;
++j;
++k;
++l;
}
else
{
s[l]=a[i]-48;
++i;
++k;
++l;
}
}
int so[3]={i,j,k};
sort(so,so+3); //p排序三个指针值,找中间那个,其代表的串剩余部分加入答案
if(so[1]==i)
{
for(i;i<2*n;i++)
{
s[l]=a[i]-48;
++l;
}
}
else if(so[1]==j)
{
for(j;j<2*n;j++)
{
s[l]=b[j]-48;
++l;
}
}
else
{
for(k;k<2*n;k++)
{
s[l]=c[k]-48;
++l;
}
}
for(int p=0;p<3*n;p++)
cout<<s[p];
cout<<endl;
}
}