前言
本来不打算写题解什么的。但是觉得很有必要。题解不是粘贴程序而是一个思考的过程。思考,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;
}