大意:确定切割木棍的次序, 代价为当前木棍长度,使得切割的总的代价最小。
思路:我想了很久,后来发现状态转移方程可以这样表示:d[i][j] = min(d[i][j], d[i][k]+d[k][j]+(a[j]-a[i])) (i<=k< j)
d[i][j]数组表示在区间[i,j]内切割木棍的最小代价,这样问题及其子问题都具有最优结构,无后效性,这样我们只要确定状态变量以及当前决策就可以表示出状态转移方程,最大代价类似。
注意一些细节,当i == j时,d[i][j] = 0,而我们求min(d[i][j])时,须把d[i][j]赋值为INF,如果求得的d[i][j]仍为INF,则赋值为0,我的理解是可能在区间[i,j]之间可能不存在a[i],所以d[i][j]必须为0,表示没有切割。
于是可以用两种方法实现,第一种类似矩阵链乘,第二种为记忆化搜索。
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <string>
using namespace std;
const int INF = 0x3f3f3f3f;
int d[51][51];
int a[51];
int L, n;
void init()
{
memset(d, 0, sizeof(d));
}
int read_case()
{
scanf("%d", &L);
if(!L) return 0;
scanf("%d", &n);
a[0] = 0, a[n+1] = L;
for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
return 1;
}
int dp()
{
init();
for(int r = 1; r <= n+1; r++) //划分阶段
{
for(int i = 0; i <= n-r+1; i++) //j-i = r, j <= n+1 -> i <= n-r+1;
{
int j = i+r; // j-i = r -> j = i+r;
d[i][j] = INF;
for(int k = i+1; k < j; k++)
{
int t = d[i][k]+d[k][j]+a[j]-a[i];
d[i][j] = min(d[i][j], t);
}
if(d[i][j] == INF) d[i][j] = 0;
}
}
return d[0][n+1];
}
void solve()
{
int ans = dp();
printf("The minimum cutting is %d.\n", ans);
}
int main()
{
while(read_case())
{
solve();
}
return 0;
}
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <string>
using namespace std;
const int INF = 0x3f3f3f3f;
int d[51][51];
int a[51];
int n, L;
bool vis[51][51];
void init()
{
memset(d, 0, sizeof(d));
memset(vis, 0, sizeof(vis));
}
int dp(int i, int j)
{
int &ans = d[i][j];
if(vis[i][j]) return ans;
vis[i][j] = 1;
ans = INF;
for(int r = i+1; r < j; r++)
{
ans = min(ans, dp(i, r) + dp(r, j) + (a[j]-a[i]));
}
if(ans == INF) ans = 0;
return ans;
}
int read_case()
{
init();
scanf("%d", &L);
if(!L) return 0;
scanf("%d", &n);
a[0] = 0, a[n+1] = L;
for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
return 1;
}
void solve()
{
int ans = dp(0, n+1);
printf("The minimum cutting is %d.\n", ans);
}
int main()
{
while(read_case())
{
solve();
}
return 0;
}