http://acm.pku.edu.cn/JudgeOnline/problem?id=1179

   Задача: Полигон

   Существует игра для одного игрока, которая начинается с задания Полигона с N вершинами. Ниже на рисунке приведен пример графического предоставления Полигона при N = 4. Для каждой вершины Полигона задается значение - целое число, а для каждого ребра - метка операции "+" (сложение), либо "*" (умножение).
http://acm.pku.edu.cn/JudgeOnline/images/1179_1.jpg
   Ребра Полигона пронумерованы от 1 до N.
   Первых ходом в игре удаляется одно из ребер.
   Каждый последующий ход состоит из следующих шагов:
   
   - выбирается ребро Е и две вершины V1 и V2, которые соединены ребром Е;
   - ребро Е и вершины V1 и V2 заменяються новой вершиной со значением, равным результату выполнения операции, определенной меткой ребра Е, над значениями вершины V1 и V2.

   Игра заканчивается, когда больше нет ни одного ребра. Результат игры - это число, равное значению оставшейся вершины.

   Пример игры. Рассмотрим полигон, приведенный в условии задачи.

   Игрок начал игру с удаления ребра 3 (рисунок ниже). После этого игрок выбирает ребро 1, затем - ребро 4 и, наконец, ребро 2. Результатом будет число 0.
http://acm.pku.edu.cn/JudgeOnline/images/1179_2.jpg

   Требуется написать программу, которая по заданному Полигону вычисляет максимальное значение оставшейся вершины и выводит список всех ребер, удаление которых на первом ходе игры позволяет получить это значение.

   Входные данные

   Входные данные опис ывают полигон с N вершинами. Данные содержаться в 2-х строках. В первой строке записано число N. Вторая строка содержит метки ребер с номерами 1, ..., N, между которыми записаны через один пробел значения вершин (первое из них соответствует вершине, смежной ребрам 1 и 2, следующее - смежной 2 и 3, и так далее, последнее - смежной ребрам N и 1). Метка ребра - это буква t, соответствующая операции "+" или буква x, соответствующая операции "*".

   3 <= N <= N50
   Для любой последовательности ходов значения вершин находятся в пределах [ -32768, 32767 ]

   Выходные данные

   В первой строке выходных данных выведите максимальное значение оставшейся вершины, которое может быть достигнуто для заданного Полигона. Во второй строке должен быть записан список всех ребер, удаление которых на первом ходе позволяет получить это значение. Номера ребер должны записываться в возрастающем порядке и отделяться друг от друга пробелом.

   Пример входных данных\

4
t -7 t 4 x 2 x 5

   Пример выходных данных

33
1 2

   Обсуждение задачи

   Рассмотрим более простой вариант задачи. Пусть операции в вершинах неотрицательны, а на ребрах мы сами выбираем операцию "+" или "*". Из значений в вершинах (операндов) и операций составляется арифметическое выражение. Задача сводиться к такой расстановке скобок в арифметическом выражении, чтобы результат вычисления этого выражения был максимальным.

   Пусть у нас есть строка s, описывающая конкретное выражение. Подзадача - это решение задачи для строки с i-го по j-й операнд. Получив один раз результат для подзадачи, мы затем в неизменном виде (без пересчета) используем его для решения подзадач большей размерности (выражения большей длины). В массиве W мы при этом храним результаты решения подзадач; W[i, j] равно максимальному значению выражения, полученному при решении задачи для подстроки s[i..j]. Значение W[i, i] рвно i-му операнду. Очевидно, что W[i, j] = 0 при i > j. Особенность заполнения массива W заключается в том, что он заполняется по диагоналям, параллельным главным: j - i = 0, j - i = 1, j - i = 2, ..., j - i = N-1. Принцип такого заполнения описывается следующими рекуррентными соотношениями:

   W[i, j] = s[i] при i = j, 1 <= i, j <= N.
   W[i, j] = Max(W[i, k] * W[k+1, j], W[i, k] + W[k+1, j]) при i < j

   Вернемся к исходной задаче. После удаления первого ребра остается выражение, в котором необходимо расставить скобки так, чтобы его значение было максимальным. В отличие от предыдущей задачи, здесь знаки операций уже заданы. Например, пусть в примере из формулировки задачи удалено первое ребро. Остается выражение: -7 + 4 * 2 * 5. Элемент массива W[i, j], исходя из особенностей задачи, мы теперь определим несколько иначе, а именно - как максимальное значение длины j, начинающегося с операнда i. Так, W[1, 3] равно максимальному значению выражения -7 + 4 * 2, достигаемого при проверке всех способов расстановки скобок. Очевидно, что W[i, 1] равно значению операнда i.

   Теперь явно просматривается традиционная динамическая схема. Единственная сложность - отрицательные числа: при их умножении может быть получен максимум. В силу этого необходимо ввести аналогичный массив V для хранения минимальных значений и также рассчитывать их для каждого выражения.

   Ответом в задаче является максимальное (максимальные) значение (значения) в последнем столбце W. При этом номера строк указывают те ребра исходного графа, удаление которых позволяет получить эти значения.

   Решение на Pascal:

Код:
const
  minVert = 3;
  maxVert = 50;
  antMaxVert = maxVert - 1;
  opSum   = 't';

type
  polygonT = record
               vert: array[0..antMaxVert] of char;
               side: array[0..antMaxVert] of integer;
               numVert: minVert..maxVert
             end;

  maxminT = record
              maxVal, minVal: integer
            end;

  memoryT = array[1..maxVert, 0..antMaxVert] of maxminT;

  scoresT = record
              scor: array[1..maxVert] of integer;
	      numVert, highScor, firstVertHS: integer;
            end;

var
  polygon: polygonT;
  scores: scoresT;


procedure ReadPolygon (var P: polygonT);
var
  i: integer;
  c: char;
begin
  with P do
    begin
      readln(numVert);
      for i := 0 to numVert - 2 do
        read(vert[i], side[i], c);
      read(vert[numVert - 1], side[numVert - 1]);
    end
end;


procedure Evaluate (Op: char; E1, E2: maxminT; var MM: maxminT);
var
  values: array[1..4] of integer;
  i, indMax, indMin: integer;
begin
  if Op = opSum then
    begin
      MM.maxVal := E1.maxVal + E2.maxVal;
      MM.minVal := E1.minVal + E2.minVal
    end
  else
    begin
      values[1] := E1.maxVal * E2.maxVal;
      values[2] := E1.maxVal * E2.minVal;
      values[3] := E1.minVal * E2.maxVal;
      values[4] := E1.minVal * E2.minVal;
      indMax := 1;
      indMin := 1;
      for i := 2 to 4 do
        if values[i] > values[indMax] then
	  indMax := i
	else if values[i] < values[indMin] then
	  indMin := i;
      MM.maxVal := values[indMax];
      MM.minVal := values[indMin]
    end
end;


procedure CompScores (var P: polygonT; var M: memoryT);
var
  lastVert, line, inf, infRight, dimLeft: integer;
  mmTemp, mmAux: maxminT;
begin
  lastVert := P.numVert - 1;

  (* Line 1 --- expressions with one integer *)
  for inf := 0 to lastVert do
    begin
      M[1, inf].maxVal := P.side[inf];
      M[1, inf].minVal := P.side[inf]
    end;

  (* Line 2 --- expressions with two integers *)
  for inf := 0 to lastVert do
    begin
      infRight := (inf + 1) mod P.numVert;
      if P.vert[infRight] = opSum then
        M[2, inf].maxVal := M[1, inf].maxVal + M[1, infRight].maxVal
      else
        M[2, inf].maxVal := M[1, inf].maxVal * M[1, infRight].maxVal;
      M[2, inf].minVal := M[2, inf].maxVal
    end;

  (* Line 3 ... Line P.numVert *)
  for line := 3 to P.numVert do
    for inf := 0 to lastVert do
      begin
        infRight := (inf + 1) mod P.numVert;
	Evaluate(P.vert[infRight], M[1, inf], M[line - 1, infRight], mmTemp);
	for dimLeft := 2 to line - 1 do
	  begin
	    infRight := (inf + dimLeft) mod P.numVert;
	    Evaluate(P.vert[infRight], M[dimLeft, inf], M[line - dimLeft,
infRight], mmAux);
	    if mmAux.maxVal > mmTemp.maxVal then
	      mmTemp.maxVal := mmAux.maxVal;
	    if mmAux.minVal < mmTemp.minVal then
	      mmTemp.minVal := mmAux.minVal
	  end;
	M[line, inf] := mmTemp
      end
end;


procedure CompHighScores (var P: polygonT; var S: scoresT);
var
  mem: memoryT;
  i, maxScor, fstVert: integer;
begin
  CompScores(P, mem);
  S.scor[1] := mem[P.numVert, 0].maxVal;
  maxScor := S.scor[1];
  fstVert := 1;
  for i := 2 to P.numVert do
    begin
      S.scor[i] := mem[P.numVert, i - 1].maxVal;
      if S.scor[i] > maxScor then
        begin
	  maxScor := S.scor[i];
	  fstVert := i
	end
    end;
  S.numVert := P.numVert;
  S.highScor := maxScor;
  S.firstVertHS := fstVert
end;


procedure WriteResults (var S: scoresT);
var
  i: integer;
begin
  with S do
    begin
      writeln(highScor : 1);
      write(firstVertHS : 1);
      for i := firstVertHS + 1 to numVert do
        if scor[i] = highScor then
	  write(' ', i : 1);
      writeln
    end
end;


begin
  ReadPolygon(polygon);
  CompHighScores(polygon, scores);
  WriteResults(scores);
end.

Решение на С++:

Код:
#include <iostream>
#include <algorithm>
using namespace std;
const int MAXN = 55;
int ans[MAXN];
int n, a[MAXN], b[MAXN];
char str1[MAXN], str2[MAXN];
int min(int a,int b){if(a<=b) return a;else return b;}
int max(int a,int b){if(a>=b) return a;else return b;}
void input()
{
       char s[10];
       scanf("%d", &n);
       for(int i = 0; i < n; i ++) {
             scanf("%s %d",s, &a[i]);
             str1[i] = s[0];
       }
}
int DP()
{
       int i, j, k,r;
       int dp[MAXN][MAXN][2];
       for(i = 0; i < n; i ++)
             dp[i][i][0] = dp[i][i][1] = b[i];
       for(r=2;r<=n;r++) 
       {
         for(i=0;i<=n-r;i++) 
         {
           j=i+r-1; 
           dp[i][j][0]=INT_MAX;
           dp[i][j][1]=INT_MIN;
            for(k=i;k<j;k++)
            {
              if(str2[k]=='t')
              {
                dp[i][j][0]=min(dp[i][j][0],dp[i][k][0]+dp[k+1][j][0]);
                dp[i][j][1]=max(dp[i][j][1],dp[i][k][1]+dp[k+1][j][1]);
              }
              else
              {
                 int t=dp[i][j][0];
                 t=min(t,dp[i][k][0]*dp[k+1][j][0]);
                 t=min(t,dp[i][k][0]*dp[k+1][j][1]);
                 t=min(t,dp[i][k][1]*dp[k+1][j][1]);
                 t=min(t,dp[i][k][1]*dp[k+1][j][0]);
                 dp[i][j][0]=t;
                 t=dp[i][j][1];
                 t=max(t,dp[i][k][0]*dp[k+1][j][0]);
                 t=max(t,dp[i][k][0]*dp[k+1][j][1]);
                 t=max(t,dp[i][k][1]*dp[k+1][j][1]);
                 t=max(t,dp[i][k][1]*dp[k+1][j][0]);
                dp[i][j][1]=t;
              }
          }
      }
    }
       return dp[0][n - 1][1];
}

int main()
{
       input();
       int i, j, mx = INT_MIN;
       for(i = 0; i < n; i ++)
       {
             for(j = 0; j < n; j ++)
             {
                  b[j] = a[(i + j) % n];
                  if(j < n-1) str2[j] = str1[(i + j + 1) % n];
             }
             ans[i] = DP();
             if(mx < ans[i])  mx = ans[i];
       }
       printf("%d\n", mx);
       for(i = 0; i < n; i ++)
             if(mx == ans[i]) printf("%d ", i + 1 );
       printf("\n");
       return(0);
}