【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的时候,枚举儿子节点。对于当前枚举的节点tt1[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;

}



 

 

 

你可能感兴趣的:(模拟)