#include<iostream>
#include<string>
using namespace std;
const int N = 1010;
string s;
int n;
int f[N][N];
int main()
{
cin >> s;
n = s.size();
s = " " + s;
for (int len = 1; len <= n; len++)
{
for (int left = 1; left + len - 1 <= n; left++)
{
int right = left + len - 1;
if (s[left] == s[right]) f[left][right] = f[left + 1][right - 1];
else f[left][right] = min(f[left + 1][right], f[left][right - 1]) + 1;
}
}
cout << f[1][n] << endl;
return 0;
}
OJ题来源:洛谷
OJ题名:Treats for the Cows(款待奶牛)
OJ题归属:动态规划【区间dp】
解题算法:动态规划
经验总结:区间dp本质特点之一:就是只对区间的左右两头端点进行操作。
cpp复制代码
#include<iostream>
using namespace std;
const int N = 2010;
int n;
int a[N];
int f[N][N];
int main()
{
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
for (int len = 1; len <= n; len++)
{
for (int i = 1; i + len - 1 <= n; i++)
{
int j = i + len - 1;
f[i][j] = max(f[i + 1][j] + a[i] * (n - len + 1), f[i][j - 1] + a[j] * (n - len + 1));
}
}
cout << f[1][n] << endl;
return 0;
}
OJ题来源:洛谷
OJ题名:石子合并(弱化版)
OJ题归属:动态规划【区间dp】
解题算法:动态规划 / 小小空间优化的动态规划
五板斧:
cpp复制代码
#include<iostream>
#include<cstring>
using namespace std;
const int N = 310;
int n;
int a[N];
int sum[N];
int f[N][N];
int main()
{
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
sum[i] = sum[i - 1] + a[i];
}
memset(f, 0x3f3f3f3f, sizeof f);
for (int i = 0; i <= n; i++) f[i][i] = 0;
for (int len = 2; len <= n; len++)
{
for (int i = 1; i + len - 1 <= n; i++)
{
int j = i + len - 1;
int t = sum[j] - sum[i - 1];
for (int k = i; k < j; k++)
{
f[i][j] = min(f[i][j], f[i][k] + f[k + 1][j] + t);
}
}
}
cout << f[1][n] << endl;
return 0;
}
//小小空间优化一下
#include<iostream>
#include<cstring>
using namespace std;
const int N = 310;
int n;
int sum[N];
int f[N][N];
int main()
{
cin >> n;
for (int i = 1; i <= n; i++)
{
int x; cin >> x;
sum[i] = sum[i - 1] + x;
}
memset(f, 0x3f3f3f3f, sizeof f);
for (int i = 0; i <= n; i++) f[i][i] = 0;
for (int len = 2; len <= n; len++)
{
for (int i = 1; i + len - 1 <= n; i++)
{
int j = i + len - 1;
int t = sum[j] - sum[i - 1];
for (int k = i; k < j; k++)
{
f[i][j] = min(f[i][j], f[i][k] + f[k + 1][j] + t);
}
}
}
cout << f[1][n] << endl;
return 0;
}
#include<iostream>
#include<cstring>
using namespace std;
const int N = 210;
int n, m;
int s[N];
int f[N][N]; //min
int g[N][N]; //max
int main()
{
cin >> n;
m = 2 * n;
for (int i = 1; i <= n; i++)
{
cin >> s[i];
// "倍增"/复写
s[i + n] = s[i];
}
//前缀和
for (int i = 1; i <= m; i++) s[i] = s[i - 1] + s[i];
//dp
memset(f, 0x3f3f3f3f, sizeof f);
memset(g, -0x3f3f3f3f, sizeof g);
for (int i = 1; i <= m; i++) f[i][i] = g[i][i] = 0;
for (int len = 1; len <= n; len++)
{
for (int i = 1; i + len - 1 <= m; i++)
{
int j = i + len - 1;
int t = s[j] - s[i - 1];
//枚举分割点
for (int k = i; k < j; k++)
{
f[i][j] = min(f[i][j], f[i][k] + f[k + 1][j] + t);
g[i][j] = max(g[i][j], g[i][k] + g[k + 1][j] + t);
}
}
}
int ret1 = 0x3f3f3f3f, ret2 = -0x3f3f3f3f;
//枚举 环->线 可能情况的左端点
for (int i = 1; i <= n; i++)
{
ret1 = min(ret1, f[i][i + n - 1]);
ret2 = max(ret2, g[i][i + n - 1]);
}
cout << ret1 << endl << ret2 << endl;
return 0;
}