【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
121

【分析】

题目的描述有些不完整,首先考试的时候没有给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 - 538 + 29 - 9154 – 18 + 22 + 74

【样例输出】

样例输出1样例输出2样例输出3
613

【数据范围】

字符串长度不超过256。

数字个数不超过40个。

【分析】

考试的时候,看到这道题我就有了不详的预感。因为当时这道题目也没有数据范围。估计是年代太久远粘啊粘的就弄丢了。利用了类似区间动归的方法。推荐先去做石子合并和数字游戏两道题目。然后就会理解我的程序。

第三题 最长回文k子串

【题目描述】

回文串是一个从前读和从后读一样的字符串。比如ABBA,MOM是回文串,但MATE不是。一个回文串可以通过修改某些位置变成一个回文串。如果一个字符串通过修改不超过k个位置变成一个回文串,那么这个字符串就被称为k回文串。一个最长并且是k回文串的子串被称为最长k回文子串。

【输入格式】

第一行一个字符串(长度不超过1000)和一个非负整数k(0<=k<=字符串长度)。字符串只包含’a’到‘z’。

【输出格式】

输出最长k回文子串的长度。

【样例输入】

样例输入1样例输入2样例输入3
abba 0mate 1zabcddcbxy 1

【样例输出】

样例输出1样例输出2样例输出3
438

【分析】

崩溃。在前两道题目不给数据范围的基础上,虽然这道题目给了范围,但是后两个样例都错了!当然我给出的样例是更正过的。

写了一个我能想到的唯一的算法。要提出的是题目中所说的修改,是变换字母。

枚举中间点,然后扩展宽度。设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
1315

【分析】

树形动态规划。

首先两个动归数组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;
}

 

 

 

转载于:https://www.cnblogs.com/sephirothlee/archive/2010/10/30/1865250.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值