动态规划
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)求解
- 递推思路:计算将第i堆至第j堆完全合并所能获得的最大得分。这是此题的关键。
- 划分阶段:以合并的次数作为标准划分阶段。
- 确定状态:f1(i,j)表示第i 堆至第j 堆合并所能获得的最大价值,f2(i,j)表示第i 堆至第j 堆合并所能获得的最小价值。
- 状态转移方程: 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