【10/30】模拟赛
前言
本来不打算写题解什么的。但是觉得很有必要。题解不是粘贴程序而是一个思考的过程。思考,think,这是IBM的motto,thinkpad的由来。
代码统一放在最后。
think…
第一题 足球比赛
【题目描述】
在2009的中国城市足球比赛中,有2^N(n <= 10)支队参加。但是有一些队在开赛前宣布了退出比赛。比赛采取的是淘汰赛。比如有4支队伍参加,那1队和2队比赛,3队和4队比赛,然后1队和2队的胜者与3队和4队的胜者争夺冠军。但是由于某些队伍退出,使得某个原本存在的比赛没有两只队伍参加。只有一支队,那么这一支队自动晋级,如果没有队伍出现,那么就根本没有比赛。比如,1队和2队退出比赛,那么就只有3队和4队的比赛,然后其胜者在原本和1队和2队的胜者的决赛中自动晋级,成为冠军。
给出哪些队退出了比赛,计算有多少场比赛中队伍自动晋级。
【输入格式】
第一行有两个数N,M。接下来有M个数,表示哪些队退出了比赛。选手编号从1到2^N。
【输出格式】
在第一行输出有多少场比赛中队伍自动晋级。
【样例输入】
样例输入1 | 样例输入2 | 样例输入3 |
2 2 3 4 |
3 5 1 2 3 4 5 |
2 1 2 |
【样例输出】
样例输出1 | 样例输出2 | 样例输出3 |
1 | 2 | 1 |
【分析】
题目的描述有些不完整,首先考试的时候没有给N<=10这个条件。
如果N<=10,那么题目解法就很明了了。我采用的方法是:利用堆一样的结构构树,即点i的儿子节点为2 * i和2 * i + 1。利用递归进行求解。
c[i]代表i节点的这场比赛是否有选手。c[i]为true代表没有。那么如果两个儿子都是true,c[i]也为true。如果一个true一个false或者都是false,那么c[i]为false。没遇到一个true一个false的情况就把答案加一。
第二题 添加括号
【题目描述】
给你一个只包含加法和减法的算术表达式。比如,1-2+3-4-5。你可以随便在这个表达式里添加括号。只要表达式合法,就能产生一个值。比如对于上面的表达式有6个不同的值。
1-2+3-4-5= -7
1-(2+3-4-5)=5
1-(2+3)-4-5= -13
1-2+3-(4-5)=3
1-(2+3-4)-5= -5
1-(2+3)-(4-5)= -3
问给能够产生多少不同的值。
【输入格式】
在第一行有一个只包含加法或减法的算术表达式。符号和数值空格分开。所有的数值都是不超过100的非负整数。
【输出格式】
在第一行输出加括号后能产生多少不同的值。
【样例输入】
样例输入1 | 样例输入2 | 样例输入3 |
1 – 2 + 3 – 4 - 5 | 38 + 29 - 91 | 54 – 18 + 22 + 74 |
【样例输出】
样例输出1 | 样例输出2 | 样例输出3 |
6 | 1 | 3 |
【数据范围】
字符串长度不超过256。
数字个数不超过40个。
【分析】
考试的时候,看到这道题我就有了不详的预感。因为当时这道题目也没有数据范围。估计是年代太久远粘啊粘的就弄丢了。利用了类似区间动归的方法。推荐先去做石子合并和数字游戏两道题目。然后就会理解我的程序。
第三题 最长回文k子串
【题目描述】
回文串是一个从前读和从后读一样的字符串。比如ABBA,MOM是回文串,但MATE不是。一个回文串可以通过修改某些位置变成一个回文串。如果一个字符串通过修改不超过k个位置变成一个回文串,那么这个字符串就被称为k回文串。一个最长并且是k回文串的子串被称为最长k回文子串。
【输入格式】
第一行一个字符串(长度不超过1000)和一个非负整数k(0<=k<=字符串长度)。字符串只包含’a’到‘z’。
【输出格式】
输出最长k回文子串的长度。
【样例输入】
样例输入1 | 样例输入2 | 样例输入3 |
abba 0 | mate 1 | zabcddcbxy 1 |
【样例输出】
样例输出1 | 样例输出2 | 样例输出3 |
4 | 3 | 8 |
【分析】
崩溃。在前两道题目不给数据范围的基础上,虽然这道题目给了范围,但是后两个样例都错了!当然我给出的样例是更正过的。
写了一个我能想到的唯一的算法。要提出的是题目中所说的修改,是变换字母。
枚举中间点,然后扩展宽度。设f[i][j]代表以i为中点,扩展出了j的长度。如果s[i + j]等于s[i – j],那么f[i][j] = f[i][j – 1]否则等于f[i][j – 1] + 1。若f[i][j]大于当前最优答案,更新答案值。
第四题 路径分离
【问题描述】
当多条路径在一个土里两两间没有公共的点和边,那么它们被认为是分离的。现在给出你一个有n个节点n-1条边连通的图G。我们想选最多k条分离的路径。你能告诉我们怎样选才能使得所有路径上的边权总和最大吗?
【输入格式】
第一行一个n(不超过60)和k(k<=n)。接下来n-1行,每行a,b,c。表示a和b之间有一条边权为c的无向边(|c|<=10000)。
【输出格式】
输出你找到的最优路径上的边权总和。
【样例输入】
样例输入1 | 样例输入2 |
6 1 1 2 5 3 2 2 3 4 6 6 3 3 2 5 1 |
6 2 1 2 5 3 2 2 3 4 6 6 3 3 2 5 1 |
【样例输出】
样例输出1 | 样例输出2 |
13 | 15 |
【分析】
树形动态规划。
首先两个动归数组f[i][j]表示以i为根的子树分为j条路径,所能达到的最大值。g[i][j]代表以i为根分为j条路径,且i点连着一条路径,所能达到的最大值。注意g[i][j]是下图左面那样的一条而不是右面那样的。
然后在处理节点i的时候,枚举儿子节点。对于当前枚举的节点t,t1[j][k]表示t之前的节点造成k这个状态且分为j条路径所达到的最大值。其中k = 0,1,2。分别对应i点不连路径,i点连一条路径且只有一条边(就是上图左面的情况),i连一条路径且为两条边(上图右面的情况)。t2[j][k]表示算上t这个点造成k这个状态且分为j条路径所达到的最大值。利用如下的枚举方法,由t1计算t2。
for (int k = 0;k < 3;++k)
for (int i = 0;i <= m;++i)
for (int j = 0;j <= m - i;++j) {
t2[i + j][k] = max(t2[i + j][k],f[t][i] + t1[j][k]);
if (k < 2)
t2[i + j][k + 1] = max(t2[i + j][k + 1],w[now][t] + g[t][j] + t1[i][k]);
其中now是当前节点,t是枚举的儿子节点。
然后用t2(或者t1)来决定f和g的值。
代码
第一题
#include <stdio.h> #define MAXN 20 bool c[1 << MAXN]; int n,m,l,ans,x; void find(int x) { if (x >= l) return; int t1 = x * 2,t2 = x * 2 + 1; find(t1); find(t2); if (c[t1] ^ c[t2]) ++ans; else if (c[t1] && c[t2]) c[x] = 1; } int main() { freopen("football.in","r",stdin); freopen("football.out","w",stdout); scanf("%d%d",&n,&m); l = 1 << n; for (int i = 1;i <= m;++i) { scanf("%d",&x); c[l + x - 1] = 1; } find(1); printf("%d\n",ans); return 0; }
第二题
#include <stdio.h> #include <string.h> int k,a0,b0,i,j,mid,x,ii,jj; int a[300]; char b[300]; int f[100][100][1000]; bool flag[30000]; int pz = 15000; char kongge,c; int main() { freopen("Parentheseses.in","r",stdin); freopen("Parentheseses.out","w",stdout); while (scanf("%d",&x) != EOF) { a[++a0] = x; f[a0][a0][0] = 1; f[a0][a0][1] = a[a0]; scanf("%c",&c); scanf("%c",&c); b[++b0] = c; scanf("%c",&c); } b0 = a0 - 1; for (k = 2;k <= a0;++k) for (i = 1;i <= a0 - k + 1;++i) { j = i + k - 1; memset(flag,0,sizeof(flag)); for (mid = i;mid < j;++mid) { for (ii = 1;ii <= f[i][mid][0];++ii) for (jj = 1;jj <= f[mid + 1][j][0];++jj) { if (b[mid] == '+') x = f[i][mid][ii] + f[mid + 1][j][jj]; else x = f[i][mid][ii] - f[mid + 1][j][jj]; if (! flag[x + pz]) { f[i][j][++f[i][j][0]] = x; flag[x + pz] = 1; } } } } printf("%d\n",f[1][a0][0]); return 0; }
第三题
#include <stdio.h> #include <string.h> #define MAXN 1010 int f[MAXN][MAXN]; int n,k,ans; char s[MAXN]; int main() { freopen("Palindrome.in","r",stdin); freopen("Palindrome.out","w",stdout); scanf("%s%d",s,&k); n = strlen(s); for (int i = n;i > 0;--i) s[i] = s[i - 1]; for (int i = 1;i <= n;++i) for (int j = 1;j <= n;++j) { int x = i - j,y = i + j; if ((x < 1) || (y > n)) break; if (s[x] == s[y]) f[i][j] = f[i][j - 1]; else f[i][j] = f[i][j - 1] + 1; if (f[i][j] > k) break; else if (y - x + 1 > ans) ans = y - x + 1; } memset(f,0,sizeof(f)); for (int i = 1;i <= n;++i) for (int j = 1;j <= n;++j) { int x = i - j + 1,y = i + j; if ((x < 1) || (y > n)) break; if (s[x] == s[y]) f[i][j] = f[i][j - 1]; else f[i][j] = f[i][j - 1] + 1; if (f[i][j] > k) break; else if (y - x + 1 > ans) ans = y - x + 1; } printf("%d\n",ans); return 0; }
第四题
#include <stdio.h> #include <string.h> #include <iostream> #define MAXN 100 using namespace std; int w[MAXN][MAXN],f[MAXN][MAXN],g[MAXN][MAXN],t1[MAXN][3],t2[MAXN][MAXN]; int n,m,x,y,z; void dp(int fa,int now) { for (int i = 1;i <= n;++i) if ((i != fa) && (w[now][i] != -1)) dp(now,i); memset(t1,0,sizeof(t1)); for (int t = 1;t <= n;++t) if ((t != fa) && (w[now][t] != -1)) { memset(t2,0,sizeof(t2)); for (int k = 0;k < 3;++k) for (int i = 0;i <= m;++i) for (int j = 0;j <= m - i;++j) { t2[i + j][k] = max(t2[i + j][k],f[t][i] + t1[j][k]); if (k < 2) t2[i + j][k + 1] = max(t2[i + j][k + 1],w[now][t] + g[t][j] + t1[i][k]); } for (int i = 0;i < MAXN;++i) for (int j = 0;j < 3;++j) t1[i][j] = t2[i][j]; } for (int i = 0;i <= m;++i) { f[now][i] = max(max(t1[i][0],t1[i - 1][1]),t1[i - 1][2]); g[now][i] = t1[i][1]; } } int main() { freopen("path.in","r",stdin); freopen("path.out","w",stdout); scanf("%d%d",&n,&m); memset(w,255,sizeof(w)); for (int i = 1;i < n;++i) { scanf("%d%d%d",&x,&y,&z); w[x][y] = w[y][x] = z; } dp(0,1); printf("%d\n",f[1][m]); return 0; }