国庆节复习(1)

动态规划

1.数字三角形(ymoj 1019 数字金字塔)

问题描述

【数据存储】
数据存储


递推:

1.每行为一个阶段
2.f(i,j)表示第i行第j列商店到起点最大和
3.状态转移方程:
    ·逆推:为f(i,j)=max{f(i+1,j)
                                    +a(i,j)
                       {f(i+1,j+1)    
int a[N][N], f[N][N]; // a是变形的数字三角形,f保存计算结果
……
for (int i=n;i>0;i--)
  for (int j=1;j<=i;j++)
    f[i][j] = max(f[i+1][j], f[i+1][j+1]) + a[i][j];
cout<<f[1][1];
    ·顺推:为f(i,j)=max{f(i-1,j)
                                    +a(i,j)
                      {f(i-1,j-1)
int a[N][N], f[N][N], result=0; // result表示计算结果
……
for (int i=1;i<=n;i++)
for (int j=1;j<=i;j++)
f[i][j] = max(f[i-1][j], f[i-1][j-1]) + a[i][j];
for (int i=1;i<=n;i++) if (f[n][i]>result) result=f[n][i];
cout<<result;

记忆化搜索——用递归代替递推

当写出状态转移方程,但是不知道如何递推计算就可以使用记忆化搜索
//逆推样例:
bool visited[N][N];
// visited[i][j]表示f[i][j]是否算过。
// 也可以用f[i][j]=-1表示没算过。其实,只要把“算过”和“没算过”区分开就可以。
int a[N][N],f[N][N];
……
int F(int x, int y)
{
if (visited[x][y]) return f[x][y]; //如果算过,直接返回结果,否则递归计算
if (x>n) return 0; //边界处理
visited[x][y] = true;
return f[x][y] = max(F[i+1][j], F[i+1][j+1]) + a[i][j];
}
……
memset(visited,0,sizeof(visited));
cout<<F(1,1);
运行较慢

记录路径

g[N][N]记录每一步的选择

int a[N][N],f[N][N],g[N][N];
void printpath(int i,int j)
{
    if(i==0||j==0)
     return ;
    else if(g[i][j]==1)
      printpath(i-1,j);
    else
      printpath(i-1,j-1);
    cout<<a[i][j]<<' ';
}

滚动数组

尽管f有n行,但只有两行参与了计算。所以可以吧f变成f[1][n]来节省空间。
计算的时候,让i和i-1对2取模

f[i%2][j] = max(f[(i-1)%2][j], f[(i-1)%2][j-1]) + a[i][j];

或者可以这样写

#define F(i,j) f[(i)%2][j] // 注意括号
……
F(i,j) = max(F(i-1,j), F(i-1,j-1)) + a[i][j];

骗分方法

①暴力搜索
int result=0;
void search(int x,int y,int depth,int sum)
{
    if(depth==n)
    {
        if(sum>result) result=sum;
        return;
    }
    else 
    {
        search(x+1,y,depth+1,sum+a[x+1][y]);
        search(x+1,y+1,depth+1,sum+a[x+1][y+1]);
    }
}
    //调用try(1,1,1,a[1][1]) 
②贪心+搜索

对于某些求最小值的问题,可以先用贪心算法得到较优解。然后开始搜索,搜索时只要搜到的比最小值大就剪枝。搜索到顶点时更新最小值。
求最大值不能用

③随机化

从最顶层开始,让向左走和向右走的概率相等。这个过程重复若干次,总有可能的得到正确答案。

int result=0;
srand(time(NULL));
for(int t=1;t<=10000;t++)
{
    int x=0,v=0;
    for(int i=1;i<=n;i++)
    {
        double p={double}rand()/(double)RAND_MAX;
        if(p<0.5) x+=0;
        else x+=1;
        v+=a[i][x];
    }
    if(v>result) result=v;
}


区间问题——石子合并

【问题描述】n 堆石子围成一圈,每堆石子的量a[i]已知。每次可以将相邻两堆合并为一堆,将合并后石子的总量记为这次合并的得分。n-1 次合并后石子成为一堆。求这n-1 次合并的得分之和可能的最大值和最小值。(n≤100,1≤i≤n)

示例


(1)环的处理方法

以某一点作为起点,按顺时针或逆时针的顺序把环上的元素复制两遍。处理时注意起点范围(0~n-1)和最大长度(n)。
例如上面的示例,可以变成:5 9 4 4 5 9 4 4,这样就包含了分别以5、9、4、4 为起点的4 个环。

(2)求解

  1. 递推思路:计算将第i堆至第j堆完全合并所能获得的最大得分。这是此题的关键。
  2. 划分阶段:以合并的次数作为标准划分阶段。
  3. 确定状态:f1(i,j)表示第i 堆至第j 堆合并所能获得的最大价值,f2(i,j)表示第i 堆至第j 堆合并所能获得的最小价值。
  4. 状态转移方程: f1(i,j)=max{f1(i,k)+f1(k+1,j)+d(i,j)}
                  f2(i,j)=min{f2(i,k)+f2(k+1,j)+d(i,j)}
    其中1≤i≤k

猜你喜欢

转载自blog.csdn.net/shm20000412/article/details/52723977
今日推荐