文章目录
BNUZ_2021_IT节题解
赛后补题链接:https://oj.bnuz.edu.cn/collections/Bh6nJnGtWXwW/problems?limit=40&page=8
A题————————博弈
B题————————数论分块
C题————————简单模拟
D题————————树形dp
E题————————dp
F题————————模拟题(思维)
G题————————平面几何
H题————————大模拟,卡int
I题————————滑动窗口(桶)
J题————————最短路模板题
K题————————思维题
A.我的回合,抽卡!
题意:
n张卡牌双方轮流拿,每次拿一张,先手想让最后两张牌的差最小,后手反之,问这个差是否小于k。
题解:
先对n张纸牌进行排序。假设取到最后两张纸牌的下标为i和j(i<j),那么,Lucy希望最后剩下的纸牌尽可能小,就会选择下标[1,i-1]和[j+1,n]的纸牌a,而Merry则会选择[i+1,j-1]的纸牌b,由于剩下n-2张牌可以抽取,而必须保证两个人抽取的纸牌数量一样,因此a和b的数量必须相等,因此j = i + (n - 2) / 2 + 1 = i + n / 2,最后枚举i维护nums[j] - nums[i]的最小值minn,判断minn和k的大小即可算出答案。
import java.util.*;
import java.io.*;
class Main {
public static void main(String[] args) {
InputStream inputStream = System.in;
OutputStream outputStream = System.out;
InputReader sc = new InputReader(inputStream);
PrintWriter out = new PrintWriter(outputStream);
int t = sc.nextInt();
int cnt = 1;
while(t-->0) {
int n = sc.nextInt();
int k = sc.nextInt();
int[] nums = new int[n];
for(int i=0; i<n; ++i) {
nums[i] = sc.nextInt();
}
Arrays.sort(nums);
int minn = 1<<30;
for(int i=0; i+n/2<n; ++i) {
minn = Math.min(minn, nums[i+n/2] - nums[i]);
}
System.out.print("Case #");
System.out.print(cnt++);
System.out.println(":");
if(minn <= k) {
System.out.println("Lucy");
} else {
System.out.println("Merry");
}
}
}
}
class InputReader {
public BufferedReader reader;
public StringTokenizer tokenizer;
public InputReader(InputStream stream) {
reader = new BufferedReader(new InputStreamReader(stream), 32768);
tokenizer = null;
}
public String next() {
while (tokenizer == null || !tokenizer.hasMoreTokens()) {
try {
tokenizer = new StringTokenizer(reader.readLine());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
return tokenizer.nextToken();
}
public int nextInt() {
return Integer.parseInt(next());
}
}
B 游戏兑换码
题解:
很明显,这里应该要用到分块。那具体怎么分块呢?可以先敲一组找找规律。
我这里用了n=50,跑了一组暴力,可以发现我们可以把相同的n/i合成一组,对于每一组相同的n/i,ans都是一样的,这里的ans=(n%i)%(n/i).求出一个块的ans之后,利用等差数列求和公式就可以。
AC代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn = 1e5;
const int mod = 1e9+7;
int main()
{
ll inv2 = 500000004;
ll ans=0;
int T=1,cas=1;
scanf("%d",&T);
while(T--){
ll n;
ll last=1;
ans=0;
scanf("%lld",&n);
for(ll i=1;i<=n;i=last+1){
last = n/(n/i);
ll x = (n)%(n/i)%mod;//每一块的答案
ll y = ((i+last)%mod*(last-i+1)%mod*inv2)%mod;
ans = (ans+x*y%mod)%mod;
if(ans>=mod)ans-=mod;
if(ans<0)ans+=mod;
}
printf("Case #%d:\n%lld\n",cas++,ans);
}
return 0;
}
C.你十七张牌能把我秒了?
题意:
20张扑克牌中最多可以有的顺子数量
题解:
连续5张牌以上称为顺子,很容易想到只取5张牌作为顺子对答案最优,设i为顺子起点,从3到10依次枚举i,答案为i到i+4中最少的扑克牌数量的总和。
#include <bits/stdc++.h>
using namespace std;
char s[30];
int nums[15];
int main() {
//freopen("data7.txt", "r", stdin);
//freopen("out7.txt", "w", stdout);
ios::sync_with_stdio(false);
cin.tie(0);
// cout.tie(0);
int cnt = 1;
int t;
//scanf("%d",&t);
cin >> t;
while(t--) {
//printf("Case #%d:\n", cnt++);
cout << "Case #" << cnt++ << ":\n";
//scanf("%s",s);
cin >> s;
memset(nums,0,sizeof(nums));
for(int i=0; i<20; ++i) {
if(s[i] >= '3' && s[i] <= '9') nums[s[i]-'0'] += 1;
if(s[i] == 'T') nums[10] += 1;
if(s[i] == 'J') nums[11] += 1;
if(s[i] == 'Q') nums[12] += 1;
if(s[i] == 'K') nums[13] += 1;
if(s[i] == 'A') nums[14] += 1;
}
int ans = 0;
for(int i=3; i<=10; ++i) {
int minn = 1<<30;
for(int j=i; j<i+5; ++j) {
minn = min(minn, nums[j]);
}
ans += minn;
for(int j=i; j<i+5; ++j) {
nums[j] -= minn;
}
}
//printf("%d\n", ans);
cout << ans << endl;
}
}
D.嘉然小姐的狗
题意:
一个树形结构的关系图,直属上级吟诵,则自己不能吟诵。每个人吟诵都能增加快乐值,求最大快乐值
解法:
对于树上的每个结点,都有选与不选两种情况,如果这个结点选了,则上级和下级都不能选。可能会出现连续两级都不选的情况。假设有一条直链:1 5 3 3 7,选5和7为最优解。
树形dp板题:从根节点开始,对树进行搜索,走遍每一条路,并且记录每一个结点的选与不选的情况。
AC代码
/*
* @Author: SolitaryOrz
* @Date: 2021-04-26 15:26:28
*/
#include<bits/stdc++.h>
using namespace std;
const int maxn = 100010;
int happy[maxn],dp[maxn][2];
vector<int> son[maxn];
void dfs(int val) {
dp[val][0] = 0;
dp[val][1] = happy[val];
for(int i=0;i<son[val].size();i++) {
int tmp = son[val][i];
dfs(tmp);
dp[val][0] += max(dp[tmp][0],dp[tmp][1]);
dp[val][1] += dp[tmp][0];
}
}
int main() {
int t, cas = 0;
cin >> t;
while(t--) {
int n;
cin >> n;
memset(dp,0,sizeof(dp));
for(int i=1;i<=n;i++) {
son[i].clear();
}
for(int i=1;i<=n;i++) {
cin >> happy[i];
}
for(int i=2;i<=n;i++) {
int num;
cin >> num;
son[num].push_back(i);
}
dfs(1);
printf("Case #%d:\n",++cas);
cout << max(dp[1][0],dp[1][1]) << endl;
}
}
E. 括号匹配
由于不能在已匹配的括号中加入括号,所以一开始可以先把已匹配的括号用一个非括号字符进行替换,这里使用*
进行替换。而且,添加左括号和添加右括号一定是独立的,所以可以进行分别计算。在预处理之后,可以将字符串分割为两个串只有左括号和*
的和只有右括号和*
。以只有(
和*
的序列为例,dp(i,j)表示从下标i到j的子串中在添加最少括号使得序列中的所有括号匹配后的所能得到的不同序列的数量(其中的i小于等于j),其中dp(i,i)为1,由题意分析可知dp(i,j) = dp(i+1,i+1)*dp(i+2,j)+dp(i+1,i+2)*dp(i+3,j)+...+dp(i+1,j-1)*dp(j,j)+dp(i+1,j)*2
,而dp[0][s.length()-1]
就是结果。
import java.util.Scanner;
public class Main {
static char[] stk = new char[505];
static int[] point = new int[505];
static int p_top;
static int top;
static String str;
private static final long mod = (long)1e9 + 7;
private static long dp[][] = new long[505][505];
static void solve(String s){
int len = s.length();
for(int i=0;i<len;i++){
for(int j=0;j<len;j++){
dp[i][j] = 0L;
}
}
for(int i=0;i<len;i++){
dp[i][i] = 1;
}
for(int str_len=2;str_len<=len;str_len++){
for(int i=0; i + str_len - 1<len; i++){
int j = i + str_len - 1;
int temp = i;
int flag = 0;
while(s.charAt(temp) == '*' && temp < j){
temp++;
flag = 1;
}
if(flag == 1){
dp[i][i + str_len - 1] = dp[temp][j];
}else{
for(int k=i+1;k<j;k++){
dp[i][j] = (dp[i][j] + (dp[i+1][k] * dp[k+1][j]) % mod) % mod;
}
dp[i][j] = ((dp[i][j] + dp[i+1][j]) % mod + dp[i+1][j]) % mod;
}
}
}
}
static int top_point() {
return point[p_top - 1];
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
str = scanner.next();
top = 1;
p_top = 1;
point[0] = 1;
// preprocessing
for (int i = 0; i < str.length(); i++) {
if (str.charAt(i) == '(') {
stk[top++] = '(';
point[p_top++] = top - 1;
}
else if (str.charAt(i) == ')') {
if (stk[top_point()] == '(') {
top = top_point();
stk[top++] = '*';
p_top--;
}
else {
stk[top++] = ')';
}
}
else {
stk[top++] = '*';
}
}
//split
int start_r = -1;//(
int start_l = -1;//)
for (int i = 1; i < top; i++) {
if (stk[i] == ')') {
start_l = i;
}
if (stk[i] == '(') {
start_r = i;
break;
}
}
long ans1 = 1L;
long ans2 = 1L;
int flag = 0;
//processing
String temp_s = new String(stk,1,top);
if (start_r != -1) {
flag = 1;
String s = temp_s.substring(start_r-1);
s = s.substring(0,s.length()-1);
solve(s);
ans1 = dp[0][s.length()-1];
}
if (start_l != -1) {
flag = 1;
String s1 = temp_s.substring(0,start_l);
char[] s = s1.toCharArray();
//inverse
for (int i = 0; i < s1.length() / 2; i++) {
char c = s[i];
s[i] = s[s1.length() - i - 1];
s[s1.length() - i - 1] = c;
}
//replaceAll
for (int i = 0; i < s1.length(); i++) {
if (s[i] == ')') {
s[i] = '(';
}
}
solve(new String(s));
ans2 = dp[0][s1.length()-1];
}
if (flag == 0) {
System.out.println(0);
}
else {
long f_ans = (ans1 * ans2) % mod;
System.out.println(f_ans);
}
}
}
F. 不可触碰的禁忌
题意:
有两种状态,发题解,传题解。每一轮,共有p+q+2分钟。教练在的q分钟内不可发,可传。其他时候的p+2分钟可发也可传。
解法:
出题人语文水平太差,很难读懂。模拟一下,把下一个人发的时间,对(p+q+2)进行取模,判断教练是否在场。如果在场则等教练走了再发。
/*
* @Author: SolitaryOrz
* @Date: 2021-05-01 12:45:28
*/
#include<bits/stdc++.h>
using namespace std;
const int maxn = 10010;
int v[maxn];
int main() {
int t, cas = 0;
cin >> t;
while(t--) {
int n,p,q;
cin >> n >> p >> q;
for(int i=0;i<n-1;i++) {
cin >> v[i];
}
int now = 0;
for(int i=0;i<n-1;i++) {
now++;
if(now % (q+1+p+1) <= q && now % (q+1+p+1) != 0) {
now -= now % (q+1+p+1);
now += q+1;
}
now = now + v[i]-1;
}
printf("Case #%d:\n",++cas);
cout << now << endl;
}
}
G.神秘的约会
题解:
Case1: 两个点直线距离不会经过圆,直接输出两个点的距离。判断方法:判断点到直线的距离是否小于直径。
Case2: 两个点直线距离会经过圆内,先计算两个点到切线的距离(勾股定理),再计算弧度角,相加就是答案了。
AC代码:
#include <iostream>
#include <cmath>
using namespace std;
struct point{
double x,y;
};
double dis(point A,point B){
return sqrt((A.x - B.x) * (A.x - B.x) + (A.y - B.y) * (A.y - B.y));
}
double disMin(point A, point B,point C)
{
double r = ((C.x - A.x)*(B.x - A.x) + (C.y - A.y)*(B.y - A.y)) / (dis(A,B)*dis(A,B));
if (r <= 0) return dis(A,C); //第一种情况, 返回AC的长
else if (r >= 1) return dis(B,C); //第二种情况, 返回BC的长度
else //第三种情况, 返回CD的长度
{
double AD = r*dis(A,B); //先求AD的长度,(AD=r*|AB|)
if(dis(A,C)-AD<=1e-6) return 0.0;
else return sqrt(dis(A,C)*dis(A,C)-AD*AD); //再勾股定理返回CD的长度
}
}
double ang(point A, point B,point C){
double angle = acos(((C.x - A.x)*(C.x - B.x)+(C.y - A.y)*(C.y - B.y))/(dis(C,A)*dis(C,B)));
return angle;
}
int main()
{
point A,B,C;
scanf("%lf %lf %lf %lf",&A.x,&A.y,&B.x,&B.y);
double r;
scanf("%lf %lf %lf",&C.x,&C.y,&r);
double dab = dis(A,B);/*A到B的直线距离*/
double dac = dis(A,C);;/*A到圆心的距离*/
double dbc = dis(B,C);/*B到圆心的距离*/
if(disMin(A,B,C)-r>=1e-6){
printf("%.6lf\n",dab);
}else{
double ans;
double d1 = sqrt(dac*dac-r*r);
double d2 = sqrt(dbc*dbc-r*r);
//中间的夹角:大夹角-两个小夹角
double arc = ang(A,B,C)-acos(r/dbc)-acos(r/dac);
double d3 = r*arc;
ans = d1+d2+d3;
printf("%.6lf\n",ans);
}
return 0;
}
H. 二元一次方程组
字符串模拟题,先将方程转ax+by+c=0的形式,注意负号处理
得到两个方程式
a1x + b1y + c1 = 0
a2x + b2y + c2 = 0
通过abc得到xy的解,推导过程如下
a1b2x + b1b2y + c1b2 = 0 =>A
a2b1x + b2b1y + c2b1 = 0 =>B
a1b2x-a2b1x + c1b2-c2b1 = 0 == A - B
x = (c2b1-c1b2) / (a1b2-a2b1)
a1a2x + b1a2y + c1a2 = 0 =>C
a2a1x + b2a1y + c2a1 = 0 =>D
b1a2y-b2a1y + c1a2-c2a1 = 0 == C - D
y = (c2a1-c1a2) / (b1a2-b2a1)
标程cpp
#include <bits/stdc++.h>
using namespace std;
void init(long a[], int n) {
for(int i = 0; i < n; i++) {
a[i] = 0;
}
}
long toNum(char equation[], int& pos, int end, int &flag){
bool sign = true;//true == 正号, false == 负号
if(equation[pos] == '-') {
sign = false;
pos++;
}
if(equation[pos] == '+') {
pos++;
}
long ans = 0;
while(pos < end && equation[pos] >= '0' && equation[pos] <= '9') {
long numTemp = equation[pos] - '0';
ans *= 10;
ans += numTemp;
pos++;
}
if(pos < end) {
if(equation[pos] == 'x') {//a
ans = ans == 0 ? 1 : ans;
flag = 0;
} else if(equation[pos] == 'y') {//b
ans = ans == 0 ? 1 : ans;
flag = 1;
} else {//c
ans = ans == 0 ? 1 : ans;
flag = 2;
}
} else {
flag = 2;
}
return sign ? ans : -ans;
}
void toPartStandard(char equation[], long abc[], int start, int end, int flag){//flag true == 正号
for(int i = start; i < end; i++) {
int whichABC = 0;
long num = toNum(equation, i, end, whichABC);
if(equation[i]=='-'||equation[i]=='+') i--;
abc[whichABC] += (flag ? num : -num);
}
}
void toStandard(char equation[], long abc[]) {
int len = strlen(equation);
int pos = -1;
for(int i = 0; i < len; i++) {
if(equation[i] == '=') {
pos = i;
break;
}
}
toPartStandard(equation, abc, 0, pos, true);
toPartStandard(equation, abc, pos+1, len, false);
}
void sol(long& x, long& y, long abc1[], long abc2[]) {
long a1 = abc1[0], b1 = abc1[1], c1 = abc1[2];
long a2 = abc2[0], b2 = abc2[1], c2 = abc2[2];
x = (c2*b1-c1*b2) / (a1*b2-a2*b1);
y = (c2*a1-c1*a2) / (b1*a2-b2*a1);
}
int main() {
char equation1[110];
char equation2[110];
int t;
cin >> t;
for(int cas = 1; cas <= t; cas++) {
cin >> equation1 >> equation2;
long abc1[3];
long abc2[3];
init(abc1, 3);
init(abc2, 3);
toStandard(equation1, abc1);
toStandard(equation2, abc2);
long x, y;
sol(x, y, abc1, abc2);
printf("Case #%d:\nx = %ld, y = %ld\n", cas, x, y);
}
}
标程Java
import java.util.Scanner;
/**
* @author arc3102
* @date 2021/5/4 15:18
*/
public class Main {
//给一个二元一次方程组的字符串计算结果
//x=2y;
//3x=4y;
//结果保证是整数
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int t = in.nextInt();
for(int cas = 1; cas <= t; cas++) {
String equation1 = in.next();
String equation2 = in.next();
long[] ans = linearEquation(equation1, equation2);
System.out.println("Case #" + cas + ":");
System.out.println("x = " + ans[0]+ ", y = " + ans[1]);
}
}
public static long[] linearEquation(String equation1, String equation2) {
// write code here
long[] tempNums1 = new long[3];//ax+by+c = 0
long[] tempNums2 = new long[3];//dx+ey+f = 0
sol(equation1, tempNums1);
sol(equation2, tempNums2);
long a1 = tempNums1[0], b1 = tempNums1[1], c1 = tempNums1[2];
long a2 = tempNums2[0], b2 = tempNums2[1], c2 = tempNums2[2];
long x = (c2*b1-c1*b2) / (a1*b2-a2*b1);
long y = (c2*a1-c1*a2) / (b1*a2-b2*a1);
return new long[]{x, y};
}
/**
*
* @param equation
* @param start
* @param end
* @param nums
* @param flag true为 + false为 -
* @return
*/
public static int toInt(String equation, int start, int end, long[] nums, boolean flag) {
int pos = start;
StringBuilder s = new StringBuilder("");
while(pos < end && equation.charAt(pos) >= '0' && equation.charAt(pos) <= '9') {
s.append(equation.charAt(pos));
pos++;
}
if(s.toString().equals("")){
s.append('1');
}
long num = Long.valueOf(s.toString());
int abc = 2;
if (pos < end) {
if(equation.charAt(pos) == 'x') {
abc = 0;
} else if(equation.charAt(pos) == 'y') {
abc = 1;
}
}
pos = pos - (abc == 2 ? 1 : 0);
nums[abc] += (flag ? 1 : -1) * num;
return pos;
}
private static void toNums(String equation, long[] nums, int start, int end) {
//abc
for(int i = start; i < end; i++) {
if(equation.charAt(i) == '-') {
i = toInt(equation, i + 1, end, nums, false);
} else if(equation.charAt(i) == '+' || equation.charAt(i) >= '0' && equation.charAt(i) <= '9') {
if(equation.charAt(i) == '+'){
i = toInt(equation, i + 1, end, nums, true);
} else {
i = toInt(equation, i, end, nums, true);
}
} else if(equation.charAt(i) == 'x') {
nums[0] += 1;
} else if(equation.charAt(i) == 'y'){
nums[1] += 1;
}
}
}
private static void sol(String equation, long[] nums) {
int len = equation.length();
// nums = new int[3];//0->a, 1->b, 2->c 转换成 ax+by+c=0;
for(int i = 0; i < len; i++) {
if(equation.charAt(i) == '=') {
long[] tempNums1 = new long[3];
long[] tempNums2 = new long[3];
//转换左边
toNums(equation, tempNums1, 0, i);
//转换右边
toNums(equation, tempNums2, i+1, len);
//右边转到左边,变换符号
for(int k = 0; k < 3; k++) {
nums[k] = tempNums1[k] - tempNums2[k];
}
break;
}
}
}
}
I.奇妙的密码
题解:
很容易想到用桶把S串的所有字符装起来,在用一个桶读入的T串,滑动窗口,用双指针去遍历T串。
两个指针遍历T串最多遍历2*T遍。
这个题目输入数据很大,第三组数据是S有1e4个a,T有9倍的1e4-1个a和1个b,最后才有1e4个a,正常暴力的话容易超时。
AC代码:
#include<map>
#include<stdio.h>
#include<iostream>
#include<string.h>
using namespace std;
#define ll long long
const int maxn = 1e5;
const int mod = 1e9+7;
int main()
{
int T,cas=1;
scanf("%d",&T);
while(T--){
char s[maxn],t[maxn];
scanf("%s%s",s,t);
int begin=0,end=0,ans=0;
int len1=strlen(s),len2=strlen(t);
bool flag = false;
map<char,int> pwd;
map<char,int> mp;
for(int i=0;i<len1;i++){
pwd[s[i]]++;
}
while(begin<=end&&end<len2){
if(pwd.find(t[end])!=pwd.end()){
mp[t[end]]++;
if(mp[t[end]]>pwd[t[end]]){
while(t[begin]!=t[end]){
mp[t[begin]]--;
begin++;
}
mp[t[begin]]--;
begin++;
}
if(end-begin+1==len1){
ans = begin;
flag = true;
break;
}
end++;
}else{
while(begin<end){
mp[t[begin]]--;
begin++;
}
begin = end+1;
end = begin;
}
}
printf("Case #%d:\n",cas++);
if(flag) printf("%d\n",ans);
else printf("-1\n");
}
return 0;
}
J 驿站选址
最短路模板题,flody或者dijkstra都可以
标程cpp
#include <bits/stdc++.h>
using namespace std;
int mapp[110][110];
int main() {
int t;
cin >> t;
for(int cas = 1; cas <= t; cas++) {
int n, m;
cin >> n >> m;
for(int i = 0; i < n; i++) {
for(int j = 0; j < n; j++) {
mapp[i][j] = -1;
}
mapp[i][i] = 0;
}
int u, v, w;
for(int i = 0; i < m; i++) {
cin >> u >> v >> w;
mapp[u][v] = mapp[v][u] = mapp[u][v] == -1 ? w : min(mapp[u][v], w);
}
for(int k = 0; k < n; k++) {
for(int i = 0; i < n; i++) {
for(int j = 0; j < n; j++) {
if(mapp[i][k] == -1 || mapp[k][j] == -1) {
continue;
}
if(mapp[i][j] == -1) {
mapp[i][j] = mapp[i][k] + mapp[k][j];
continue;
}
mapp[i][j] = min(mapp[i][j], mapp[i][k] + mapp[k][j]);
}
}
}
long ans = (1L<<30);
for(int i = 0; i < n; i++) {
long cnt = 0;
for(int j = 0; j < n; j++) {
cnt += mapp[i][j];
}
ans = min(ans, cnt);
}
printf("Case #%d:\n%d\n", cas, ans);
}
}
标程java
import java.util.Arrays;
import java.util.Scanner;
/**
* @author arc3102
* @date 2021/5/14 11:26
*/
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int t = in.nextInt();
for(int cas = 1; cas <= t; cas++) {
int n = in.nextInt();
int m = in.nextInt();
int u, v, w;
int[][] mapp = new int[n][n];
for(int i = 0; i < n; i++) {
Arrays.fill(mapp[i], -1);
mapp[i][i] = 0;
}
for(int i = 0; i < m; i++) {
u = in.nextInt();
v = in.nextInt();
w = in.nextInt();
if(mapp[u][v] == -1) {
mapp[u][v] = mapp[v][u] = w;
} else {
mapp[u][v] = mapp[v][u] = Math.min(w, mapp[u][v]);
}
}
for(int k = 0; k < n; k++) {
for(int i = 0; i < n; i++) {
for(int j = 0; j < n; j++) {
if(mapp[i][k] == -1 || mapp[k][j] == -1) {
continue;
}
if(mapp[i][j] == -1) {
mapp[i][j] = mapp[i][k] + mapp[k][j];
continue;
}
mapp[i][j] = Math.min(mapp[i][j], mapp[i][k] + mapp[k][j]);
}
}
}
int ans = Integer.MAX_VALUE;
for(int i = 0; i < n; i++) {
int cnt = 0;
for(int j = 0; j < n; j++) {
cnt += mapp[i][j];
}
ans = Math.min(ans, cnt);
}
System.out.println("Case #" + cas + ":");
System.out.println(ans);
}
}
}
K.括号翻转
这个题解法不唯一,只讲其中一种。
先计算字符串的长度,如果为奇数则一定不可能让所有的括号匹配,直接输出-1。如果为偶数,先把已匹配的所有括号去掉,然后将其分为两个串,一个字符串只包含左括号,另一个字符串只包含右括号,初始化ans=0,如果其中一个串的长度为奇数,即预处理后的字符串中某一处为)(,需要翻转2次,则ans+=2。剩余的只需翻转一半的括号就可以让所有括号匹配,ans再加上两个串的长度整除2就是答案。
#include <stdio.h>
#include <string.h>
int main(){
int t;
char str[405];
scanf("%d",&t);
for(int i=0;i<t;i++){
scanf("%s",str);
printf("Case #%d:\n",i+1);
if(strlen(str) % 2 != 0){
puts("-1");
continue;
}
char stk[405];
int top = -1;
for(int j=0;j<strlen(str);j++){
if(top == -1){
stk[++top] = str[j];
}else if(str[j] == '('){
stk[++top] = '(';
}else if(str[j] == ')'){
if(stk[top] == '('){
top--;
} else{
stk[++top] = ')';
}
}
}
int ans = 0;
int start_l = top + 1;
int start_r = -1;
for(int j=0;j<top+1;j++) {
if (stk[j] == ')') {
start_r = j;
}
if (stk[j] == '(') {
start_l = j;
break;
}
}
int len_r = start_r + 1;
int len_l = top + 1 - start_l;
if (len_r % 2 != 0){
ans += 2;
}
ans += len_r / 2 + len_l / 2;
printf("%d\n",ans);
}
}