文章目录
- A. Average Height
- B. TMT Document
- C. The Sports Festival
A. Average Height
- 解题思路
水题,题目意思其实要求相邻数之和能凑出更多的偶数,那必然是奇数+奇数,偶数+偶数。将奇数和偶数分别存储起来输出即可。 - AC代码
/**
*@filename:A_Average_Height
*@author: pursuit
*@created: 2021-04-16 22:36
**/
using namespace std;
typedef long long ll;
const int maxn = 100000 + 5;
const int mod = 1e9+7;
int t,n;
vector<int> odd,even;
void solve(){
for(int i=0;i<odd.size();i++){
cout<<odd[i]<<" ";
}
for(int i=0;i<even.size();i++){
cout<<even[i]<<" ";
}
cout<<endl;
}
int main(){
while(cin>>t){
while(t--){
cin>>n;
int temp;
odd.clear(),even.clear();
for(int i=0;i<n;i++){
cin>>temp;
if(temp%2)odd.push_back(temp);
else even.push_back(temp);
}
solve();
}
}
return 0;
}
B. TMT Document
解题思路
此题我采用模拟的方式去做这道题。我们来细细分析,按照模拟的方法,我们会去配对TMT
。用一个哈希表来记录字符出现的次数,那么在遍历字符串的过程中,我们就可以开始配对,配对的标志其实就是围绕M
这个字符,倘若我们遇到了M
,那么我们需要做的就是寻找左右边的T
,对于左边,我们毫无顾忌,找一个T
和它配对即可,即是利用哈希表中的T
数量来判断, 而对于右边,即是当我们碰到T
的时候,我们开始抉择了,如果此时的M
是大于的,那么这个时候我们需要配对
M
,但是,并不意味着我们需要去用此时的T
来配对,因为这个T
很可能就是下一个M
左边的T
,而且右边的T
出现在哪都可以与当前的M
配对,所以我们假设它配对成功,并统计它所需要的T
,最后我们遍历完之后判断哈希表中未用的T
是否刚好是cnt
,故此题得解。需要注意的是:我们总能证明最后剩下的T
一定是在M
的右边,不然我们不会累加,注意这段程序:
if(p['M']>0){
p['M']--;//已经被配对。
//这个T可以在任何位置,存储需要的t。
cnt++;
}
AC代码
/**
*@filename:B_TMT_Document
*@author: pursuit
*@created: 2021-04-16 22:46
**/
using namespace std;
typedef long long ll;
const int maxn = 100000 + 5;
const int mod = 1e9+7;
int t,n;
string s;
map<char,int> p;
void solve(){
p.clear();
bool flag=true;
int cnt=0;
for(int i=0;i<n;i++){
p[s[i]]++;
if(s[i]=='M'){
if(p['T']==0){
flag=false;
break;
}
else{
p['T']--;
}
}
else{
if(p['M']>0){
p['M']--;
//这个T可以在任何位置,存储需要的t。
cnt++;
}
}
}
p['T']-=cnt;
if(p['T']!=0||p['M']!=0)flag=false;
if(flag)puts("YES");
else puts("NO");
}
int main(){
while(cin>>t){
while(t--){
cin>>n>>s;
solve();
}
}
return 0;
}
C. The Sports Festival
- 解题思路
这道题非常有意思。我们来看,,单看这个可能难以发现什么。那么我们直接可以看
,这个值是已经确定了的,即是所有成员的最大速度和最小速度之差,有了这个初始值,我们就好分析了,直接从初始值下手,也就是逆着来看整个过程。为了减小这个差值,我们有两步操作可以选择:
- 剔除当前最小值。
- 剔除当前最大值。
这种决策问题正好提示了我们需要使用动态规划来解决问题,即哪一步最优。我们先要表示状态,既然是剔除,我们就可以用来表示剔除了
个最大值,
个最小值的速度之差,这个值表示的意思就是累加了从
的速度差值。那么初始状态自然就是
(下标从0开始)。
那么对于状态转移方程其实就已经好确定了,,为什么是这样呢?因为
是由
或者
转移过来的,所以对于
,它需要删除最大值,那么由于已经删除了
个最大值,那么此时再删除一个则最大值就是
,最小值就是
,同理对于
,它需要删除最小值,那么由于已经删除了
个最小值,此时再删除一个,最小值就是
,最大值就是
。对于
我们同样还需要作个约束,因为最后一定是剩余一个元素的,所以删除次数不得超过
,即
,这也说明了最后状态就是
。我们这样去处理即可,注意初始化。
- AC代码
/**
*@filename:C_The_Sports_Festival
*@author: pursuit
*@created: 2021-04-16 23:05
**/
using namespace std;
typedef long long ll;
const int maxn = 2000 + 5;
const int mod = 1e9+7;
int n;
int a[maxn];
ll dp[maxn][maxn];
void solve(){
//为了更好的处理,我们需要先对数组进行升序排列,这样可以好确定最小值和最大值。
sort(a,a+n);
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
dp[i][j]=1e12;//初始dp数组保留最大值。
}
}
//初始化dp数组。
dp[0][0]=a[n-1]-a[0];//这是毋庸置疑的。
for(int i=1;i<n;i++){
//只删除最大值i次
dp[i][0]=dp[i-1][0]+a[n-i-1]-a[0];//删除最大值从右边删除,那么删除之后当前最大值就是a[n-i-1];
//只删除最小值i次
dp[0][i]=dp[0][i-1]+a[n-1]-a[i];//删除最小值从左边删除,那么删除之后当前最小值就是a[i]。
}
//接下来开始进行状态转移取最优。
for(int i=1;i<n;i++){
for(int j=1;i+j<n;j++){
//i+j不得超过n次。
dp[i][j]=min(dp[i][j],dp[i-1][j]+a[n-i-1]-a[j]);//选择删除最大值
dp[i][j]=min(dp[i][j],dp[i][j-1]+a[n-i-1]-a[j]);//选择删除最小值
}
}
ll res=1e12;
for(int i=0;i<n;i++){
res=min(res,dp[i][n-i-1]);
}
cout<<res<<endl;
}
int main(){
while(cin>>n){
for(int i=0;i<n;i++)cin>>a[i];
solve();
}
return 0;
}