首先预处理出所有的f[i][i],然后去解决f[l][r]
for(int d=1;d<=n;d++) {
for(int i=1,j=d+1;j<=n;i++,j++) {
}
}
https://www.luogu.org/problemnew/show/P4170
给定一个初始为空白的木板,一次可以给连续的一段染色,问最少染多少次,可将木板染成指定颜色。
设f[l,r]表示将[l,r]区间涂成正确颜色需要的次数
/*** author: zxwsbg ***/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define INIT(x) memset(x,0,sizeof(x))
#define eps 1e-8
#define next next_
typedef long long ll;
typedef unsigned long long ull;
const int INF = 0x7fffffff;
const int inf = 0x3f3f3f3f;
const int maxn = 200005;
const int N = 105;
inline void read(int &x) {
int f=1;x=0;char s=getchar();
while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
x*=f;
}
string s;
int f[55][55], n;
int main() {
ios::sync_with_stdio(false);
cin >> s;
n = s.length();
memset(f,0x3f,sizeof(f));
for(int i=0;i<n;i++) f[i][i] = 1;
for(int d=1;d<=n;d++) {
for(int l=0,r=d;r<n;l++,r++) {
if(s[l]==s[r]) f[l][r] = min(f[l+1][r],f[l][r-1]);
else for(int k=l;k<r;k++) f[l][r] = min(f[l][r],f[l][k]+f[k+1][r]);
}
}
cout<<f[0][n-1];
return 0;
}
https://www.luogu.org/problemnew/show/P1430
给定一个长为n的整数序列(n<=1000),由A和B轮流取数(A先取)。每个人可从序列的左端或右端取若干个数(至少一个),但不能两端都取。所有数都被取走后,两人分别统计所取数的和作为各自的得分。假设A和B都足够聪明,都使自己得分尽量高,求A的最终得分。
求A的最高得分,可以化作求A-B的最高得分,因为A-B=A-(sum-A)=2A-sum,而sum又是一个定值。
令f[l][r]表示当剩余区间为[l,r]时,A-B得到的最大值。
那么就有以下三种取法:
/*** author: zxwsbg ***/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define INIT(x) memset(x,0,sizeof(x))
#define eps 1e-8
#define next next_
typedef long long ll;
typedef unsigned long long ull;
const int INF = 0x7fffffff;
const int inf = 0x3f3f3f3f;
const int maxn = 1005;
const int N = 105;
inline void read(int &x) {
int f=1;x=0;char s=getchar();
while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
x*=f;
}
int t,f[maxn][maxn],a[maxn],n,sum[maxn];
inline int getsum(int l,int r) {return sum[r]-sum[l-1];}
int main() {
ios::sync_with_stdio(false);
read(t);
while(t--) {
read(n);
for(int i=1;i<=n;i++) read(a[i]);
INIT(f);
INIT(sum);
for(int i=1;i<=n;i++) sum[i] = sum[i-1] + a[i];
for(int d=1;d<=n;d++) {
for(int i=1,j=d;j<=n;i++,j++) {
f[i][j] = getsum(i,j);
for(int l=i+1;l<=j;l++)
f[i][j] = max(f[i][j],getsum(i,l-1)-f[l][j]);
for(int r=j-1;r>=i;r--)
f[i][j] = max(f[i][j],getsum(r+1,j)-f[i][r]);
}
}
cout<<(f[1][n]+sum[n])/2<<endl;
}
return 0;
}
上述的时间复杂度为O(n^3),明显无法通过,因此需要优化。
可以将状态转移的过程优化掉,以从右边取为例,原先需要枚举 l ≤ r ′ ≤ r l\leq r' \leq r l≤r′≤r,而f[l][r] = sum[r’+1,r] - f[l,r’] = sum[r]-sum[r’]-f[l][r’], sum[r]是固定的,而r’的状态是可以通过前面的递推出来的。
/*** author: zxwsbg ***/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define INIT(x) memset(x,0,sizeof(x))
#define eps 1e-8
#define next next_
typedef long long ll;
typedef unsigned long long ull;
const int INF = 0x7fffffff;
const int inf = 0x3f3f3f3f;
const int maxn = 1005;
const int N = 105;
inline void read(int &x) {
int f=1;x=0;char s=getchar();
while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
x*=f;
}
int t,f[maxn][maxn],a[maxn],n,sum[maxn],Min[maxn][maxn],p[maxn][maxn],P[maxn][maxn],Max[maxn][maxn];
inline int getsum(int l,int r) {return sum[r]-sum[l-1];}
int main() {
ios::sync_with_stdio(false);
read(t);
while(t--) {
read(n);
for(int i=1;i<=n;i++) read(a[i]);
for(int i=1;i<=n;i++) sum[i] = sum[i-1] + a[i];
for(int i=1;i<=n;i++) {
f[i][i] = a[i];
Min[i][i] = sum[i] + f[i][i];
Max[i][i] = sum[i-1] - f[i][i];
}
for(int d=1;d<=n;d++) {
for(int i=1,j=d+1;j<=n;i++,j++) {
f[i][j] = sum[j] - sum[i-1];
f[i][j] = max(f[i][j],Max[i+1][j]-sum[i-1]);
f[i][j] = max(f[i][j],sum[j]-Min[i][j-1]);
Min[i][j] = min(Min[i][j-1],sum[j] + f[i][j]);
Max[i][j] = max(Max[i+1][j],sum[i-1] - f[i][j]);
}
}
cout<<(f[1][n]+sum[n])/2<<endl;
}
return 0;
}