魔方吧·中文魔方俱乐部

 找回密码
 注册
搜索
热搜: 魔方
查看: 131766|回复: 11
打印 上一主题 下一主题

探讨“ 两头取数的问题” 的程序设计思路 [复制链接]

Rank: 1

积分
171
帖子
132
精华
0
UID
30495
性别
保密
1#
发表于 2009-4-4 00:02:12 |显示全部楼层
算法设计起来并不难,这道题应该是用动态规划。用f( i, j )来表示第i个数到第j个数的先拿的人能拿到的最大值,这样有状态转移方程f( i, j ) = max{ sum( i, j ) - f( i + 1, j ), sum( i, j ) - f( i, j - 1 ) },sum( i, j )表示第i个数到第j个数的和,这样就可以递推了。

使用道具 举报

Rank: 1

积分
171
帖子
132
精华
0
UID
30495
性别
保密
2#
发表于 2009-4-4 00:17:05 |显示全部楼层
如果n是偶数,谁先手谁肯定不会输,因为先手的人必然可以取得所有奇数位置上的数或者偶数位置上的数。当n是奇数的时候就没那么简单了。

使用道具 举报

Rank: 1

积分
171
帖子
132
精华
0
UID
30495
性别
保密
3#
发表于 2009-4-4 00:34:12 |显示全部楼层
楼上的递推公式显然错了。
首先当i等于j的时候f( i, j )可以确定,可以从这里出发,自底向上,也可以用递归自顶向下。

使用道具 举报

Rank: 1

积分
171
帖子
132
精华
0
UID
30495
性别
保密
4#
发表于 2009-4-4 11:45:44 |显示全部楼层
楼上那个数列,举几个例子。
f( 1, 1 ) = 47,f( 2, 2 ) = 30,f( 3, 3 ) = 1,因为只有一个数先拿者必拿到这么多。
f( 1, 2 ) = max{ 47 + 30 - f( 1, 1 ), 47 + 30 - f( 2, 2 ) } = 47
f( 2, 3 ) = max{ 30 + 1 - f( 2, 2 ), 30 + 1 - f( 3, 3 ) } = 30
f( 1, 3  ) = max{ 47 + 30 + 1 - f( 1, 2 ), 47 + 30 + 1 - f( 2, 3 ) } = 48
这样一直推到f( 1, n )就是先拿的人在双方不出错的情况下所能拿到的最大数值。

使用道具 举报

Rank: 1

积分
171
帖子
132
精华
0
UID
30495
性别
保密
5#
发表于 2009-4-4 12:12:30 |显示全部楼层
  1. #include <cstdio>

  2. int min( int a, int b ) { return ( a < b )? a: b; }

  3. int main( )
  4. {
  5.         int a[ 101 ], f[ 101 ][ 101 ], n, i, j, sum[ 101 ];
  6.         scanf("%d", &n);
  7.         for ( i = 1; i <= n; i++ )
  8.                 scanf("%d", &f[ i ][ i ]);
  9.         sum[ 0 ] = 0;
  10.         for ( i = 1; i <= n; i++ )
  11.                 sum[ i ] = sum[ i - 1 ] + f[ i ][ i ];
  12.         for ( i = 1; i < n; i++ )
  13.                 for ( j = 1; j + i <= n; j++ )
  14.                         f[ j ][ i + j ] = sum[ i + j ] - sum[ j - 1 ] - min( f[ j + 1 ][ i + j ], f[ j ][ i + j - 1 ] );
  15.         printf("%d\n", f[ 1 ][ n ]);
  16.         return 0;
  17. }
复制代码
程序写起来很简单

使用道具 举报

Rank: 1

积分
171
帖子
132
精华
0
UID
30495
性别
保密
6#
发表于 2009-4-4 20:08:05 |显示全部楼层
你解的答案是对的,你的状态和我的状态定义不同是我看漏了,不过我不得不说你这个状态的定义有点复杂了。

使用道具 举报

Rank: 1

积分
171
帖子
132
精华
0
UID
30495
性别
保密
7#
发表于 2009-4-4 20:33:39 |显示全部楼层
是的,我的程序就是这么写的

使用道具 举报

Rank: 1

积分
171
帖子
132
精华
0
UID
30495
性别
保密
8#
发表于 2009-4-4 21:42:46 |显示全部楼层
同是O(n^2)的算法,可以算一样长,但是为什么你这个计算的这么慢,这组数据n=18,我敢说肯定0,01秒内就能出解。

使用道具 举报

Rank: 1

积分
171
帖子
132
精华
0
UID
30495
性别
保密
9#
发表于 2009-4-4 22:01:40 |显示全部楼层
看来你还没有理解啊。。。。。
动态规划的主要思想就是避免重复计算,你可以这么想,为了确定第1步我们要计算出所有的来比较,第2步我们就不用再计算了,因为第1步已经计算过了。

使用道具 举报

Rank: 1

积分
171
帖子
132
精华
0
UID
30495
性别
保密
10#
发表于 2009-4-4 22:15:39 |显示全部楼层
我贴个伪码
  1. input n
  2. sum( 0 ) := 0
  3. for i := 1 to n
  4. begin
  5.         input a( i )
  6.         f( i, i ) := a( i )
  7.         sum( i ) := sum( i - 1 ) + a( i )
  8. end
  9. for i := 1 to n
  10.         for j := 1 to n
  11.                 f( j, i + j ) = sum( i + j ) - sum( j - 1 ) - min( f( j + 1, i + j ), f( j, i + j - 1 ) )
  12. print f( 1, n ), sum( n ) - f( 1, n )        //f( 1, n )是S甲,sum( n ) - f( 1, n )是S乙

  13. //下面开始输出每个数第几次被取走
  14. start := 1
  15. end := n
  16. for i := 1 to n
  17. begin
  18.         if f( start + 1, end ) < f( start, end - 1 )
  19.         begin
  20.                 p( start ) := i
  21.                 start := start + 1
  22.         end
  23.         else
  24.         begin
  25.                 p( end ) := i
  26.                 end := end - 1
  27.         end
  28. end
  29. for i := 1 to n
  30.         print p( i )
复制代码

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

Archiver|手机版|魔方吧·中文魔方俱乐部

GMT+8, 2024-5-19 04:40

Powered by Discuz! X2

© 2001-2011 Comsenz Inc.

回顶部