原题链接:删删
思路:直接遍历从a到z的所有字母,依次判断只删除这些字母能不能构成回文串,并取构成回文串时删除的字符的最小值。具体地,用两个指针分别指向字符串的头和尾,然后向中间移动指针,如果遇到相应的字母且两指针处的字符不相等则跳过该字符,直到两指针相遇时如果指针所指的字符均相等则说明能构成回文串。
代码:
#include
using namespace std;
int t, n, ans;
string s;
int main(){
cin >> t;
for(int i = 0; i < t; i++){
cin >> n >> s;
ans = 0x3f3f3f3f;
bool flag = false;
for(int j = 'a'; j <= 'z'; j++){
int l = 0, r = n - 1, tmp = 0;
bool flag1 = true;
while(l < r){
if(s[l] != s[r]){
if(s[l] == j) l++, tmp++;
else if(s[r] == j) r--, tmp++;
else{
flag1 = false;
break;
}
}
else l++, r--;
}
if(flag1) ans = min(ans, tmp), flag = true;
}
if(!flag) cout << -1 << endl;
else cout << (ans == 0x3f3f3f3f ? 0 : ans)<< endl;
}
return 0;
}
原题链接:快快变大
思路:区间dp,先对数组中连续的乘积预处理,用mul[i][j]表示从ai乘到aj的乘积,然后直接套用区间dp模板:
dp[i][j] = max(dp[i][j], dp[i][k] + dp[k + 1][j] + (mul[i][k] - mul[k + 1][j]) * (mul[i][k] - mul[k + 1][j]));
其中dp[i][j]表示ai到aj区间的最大分数。
代码:
#include
using namespace std;
long long n, a[500], dp[500][500], mul[500][500];
int main(){
cin >> n;
for(int i = 1; i <= n; i++){
cin >> a[i];
mul[i][i] = a[i];
for(int j = 1; j < i; j++){
mul[j][i] = (mul[j][i - 1] * a[i]) % 1000003;
}
}
for(int len = 2; len <= n; len++){
for(int i = 1; i + len - 1 <= n; i++){
int j = i + len - 1;
for(int k = i; k < j; k++){
dp[i][j] = max(dp[i][j], dp[i][k] + dp[k + 1][j] + (mul[i][k] - mul[k + 1][j]) * (mul[i][k] - mul[k + 1][j]));
}
}
}
cout << dp[1][n];
return 0;
}
原题链接:饿饿 饭饭2
思路:如果两个数乘上2或3能变成一个相同的数,那么反过来想,用这个相同的数一直除2或3,那么最后一定能除成一个不能被2和3整除的数,说明这两个数分别除2或3也能除成一个相同的且不能被2和3整除的数。按照次思路写代码即可。
代码:
#include
using namespace std;
int t, n, a[200005];
int main(){
cin >> t;
for(int i = 0; i < t; i++){
cin >> n;
bool flag = true;
for(int j = 1; j <= n; j++){
cin >> a[j];
}
for(int j = 1; j <= n; j++){
while(a[j] % 2 == 0 || a[j] % 3 == 0){
if(a[j] % 2 == 0) a[j] /= 2;
if(a[j] % 3 == 0) a[j] /= 3;
}
}
for(int j = 1; j < n; j++){
if(a[j] != a[j + 1]){
flag = false;
break;
}
}
if(flag) cout << "YES\n";
else cout << "NO\n";
}
return 0;
}
原题链接:子串分值和
思路:依题意,所有子串中出现的不同字符的数量即为所有包含某一种字符的子串的总和,求出所有的只包含某一种字符的子串数量即可。
代码:
#include
using namespace std;
string s;
long long ans;
int main(){
cin >> s;
for(int i = 'a'; i <= 'z'; i++){
long long len = 0, tmp = 0;
for(int j = 0; j < s.size(); j++){
if(s[j] == i){
tmp += len * (len + 1) / 2;
len = 0;
}
else len++;
}
tmp += len * (len + 1) / 2;
ans += s.size() * (s.size() + 1) / 2 - tmp;
}
cout << ans;
return 0;
}
原题链接:蒟蒻
思路:用两个map存储果冻的价格与口感,分别为map<价格,口感>,map<口感,价格>,则操作1为给map容器赋值,操作2和操作3为用map中begin()方法取出第一个元素,判断重复时使用count()方法即可。
代码:
#include
using namespace std;
int n, ans;
map<int, int> mp1, mp2;
int main(){
cin >> n;
for(int i = 0; i < n; i++){
int op;
cin >> op;
if(op == 1){
int w, t;
cin >> w >> t;
if(mp1.count(w) == 0 && mp2.count(t) == 0) mp1[w] = t, mp2[t] = w;
}
else if(op == 2){
mp2.erase(mp1.begin()->second);
mp1.erase(mp1.begin()->first);
}
else if(op == 3){
mp1.erase(mp2.begin()->second);
mp2.erase(mp2.begin()->first);
}
}
for(auto w : mp2){
ans += w.second;
}
cout << ans;
return 0;
}
原题链接:锦标赛
思路:由题意,两者能力值之差大于k时则能力值高的人必胜,若差值小于等于k则都有可能获胜,那么对于以能力值排序后的数组,从能力值最大的人开始遍历,如果有俩人能力值之差大于k,则从这两人中能力值低的人开始,往后的能力值更低的人则不可能获胜。此时可能获胜的人即为前面的所有人。
代码:
#include
using namespace std;
int n, k, a[100005], ans;
bool check(int i, int j){
return fabs(a[i] - a[j]) <= k;
}
int main(){
cin >> n >> k;
for(int i = 1; i <= n; i++){
cin >> a[i];
}
sort(a + 1, a + 1 + n);
for(int i = n; i > 1; i--){
if(check(i, i - 1)) ans++;
else break;
}
cout << ans + 1;
return 0;
}
原题链接:可重排列
思路:简单的dfs,回溯条件为输出的总数达到p1 + p2 + … + pn。
代码:
#include
using namespace std;
int n, a[10], tmp[100], sum;
void dfs(int cnt){
if(cnt == sum){
for(int i = 0; i < cnt; i++) printf("%d ", tmp[i]);
printf("\n");
return;
}
for(int k = 1; k <= n; k++){
if(a[k]){
a[k]--;
tmp[cnt] = k;
dfs(cnt + 1);
a[k]++;
}
}
return;
}
int main(){
cin >> n;
for(int i = 1; i <= n; i++){
cin >> a[i];
sum += a[i];
}
dfs(0);
return 0;
}
原题链接:进制转换
思路:简单地模拟进制的转换过程即可。
代码:
#include
using namespace std;
int n, m;
long long sum;
string ans = "";
int main(){
cin >> n >> m;
for(int i = 0; i < n; i++){
int t, tmp = 0;
string x;
cin >> t >> x;
for(int j = 0; j < x.size(); j++){
if(x[j] >= '0' && x[j] <= '9') tmp = tmp * t + (x[j] - '0');
else if(x[j] >= 'A' && x[j] <= 'Z') tmp = tmp * t + (x[j] - 'A' + 10);
else if(x[j] >= 'a' && x[j] <= 'z') tmp = tmp * t + (x[j] - 'a' + 36);
}
sum += tmp;
}
while(sum){
int tmp = sum % m;
if(tmp <= 9) ans.push_back(tmp + '0');
else if(tmp >= 10 && tmp <= 35) ans.push_back(tmp - 10 + 'A');
else if(tmp >= 36 && tmp <= 61) ans.push_back(tmp - 36 + 'a');
sum /= m;
}
reverse(ans.begin(), ans.end());
cout << ans;
return 0;
}
原题链接:循环子串
思路:由题意,如果一个字符串的所有子串的倒转串均为原字符串的循环子串并且这个字符串的最长子串(即字符串本身)的倒转串也为原字符串的循环子串,那么该最长子串的所有子串一定也为原字符串的循环子串。因此只需要判断字符串本身的倒转串是否为原字符串的循环子串即可。
代码:
#include
using namespace std;
int t, n;
string s;
int main(){
cin >> t;
for(int i = 0; i < t; i++){
cin >> n >> s;
string tmp = s;
reverse(tmp.begin(), tmp.end());
bool flag = false;
for(int j = 0; j < n; j++){
char ch = s[n - 1];
for(int k = n - 1; k >= 1; k--){
s[k] = s[k - 1];
}
s[0] = ch;
if(tmp == s){
flag = true;
break;
}
}
if(flag) cout << "YES\n";
else cout << "NO\n";
}
return 0;
}
原题链接:饿饿 饭饭之暑假大狂欢
思路:最有益于某个人的策略即为每次抽出的球的数字都为某个人手里的数字,那么直接遍历所有人,并判断划掉所有的数字时其他人有没有划完所有数字即可。
代码:
#include
using namespace std;
int t, n, a;
vector<int> vec[105];
bool vis[105][105];
int main(){
cin >> t;
for(int i = 1; i <= t; i++){
cin >> n;
for(int j = 0; j < n; j++){
cin >> a;
vec[i].push_back(a);
}
}
for(int i = 1; i <= t; i++){
bool flag = true;
memset(vis, 0, sizeof(vis));
for(int j = 1; j <= t; j++){
if(j == i) continue;
int sum = 0;
for(int k = 0; k < vec[i].size(); k++){
for(int h = 0; h < vec[j].size(); h++){
if(vec[i][k] == vec[j][h] && !vis[j][h]){
sum++;
vis[j][h] = true;
}
}
}
if(sum == vec[j].size()){
flag = false;
break;
}
}
if(flag) cout << "YES\n";
else cout << "NO\n";
}
return 0;
}