P1430 序列取數(區間DP)
思路:區間。想了好半天+看題解才搞明白,這裏再梳理一遍。
顯然當的取值和最大時,因爲常數,所以最大。
令爲區間的最大差值。
因爲。
所以
當先手取左區間,剩餘區間爲,時有轉移方程:
。
表示的是先手只能取得左區間的數和後面的狀態(之前的後手變爲先手的最大差值)。
右區間同理,剩餘區間爲:
.
因爲這樣是的,所以考慮優化。
顯然我們可以預處理左區間計算時的的最大值和右區間的的最小值。
然後對這兩個進行區間更新維護即可。
時間複雜度:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e3+5,M=1e6+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a) memset(a,0,sizeof a)
#define lx x<<1
#define rx x<<1|1
#define reg register
#define PII pair<int,int>
#define fi first
#define se second
int mx[N][N],mn[N][N],f[N][N],pre[N],a[N];
//mx[i][j] pre[k-1]-f[k][j] k 屬於[i+1,j]
//mn[i][j] pre[k]+f[i][k] k屬於[i,j-1]
int main(){
int t;
scanf("%d",&t);
while(t--){
int n;
scanf("%d",&n);
for(int i=0;i<=n+1;i++){
pre[i]=0;
for(int j=1;j<=n+1;j++) f[i][j]=mx[i][j]=mn[i][j]=0;
}
for(int i=1;i<=n;i++) scanf("%d",&a[i]),pre[i]=pre[i-1]+a[i];
for(int i=1;i<=n;i++){
f[i][i]=a[i],mx[i][i]=pre[i-1]-f[i][i];
mn[i][i]=pre[i]+f[i][i];
}
for(int k=2;k<=n;k++)
for(int l=1,r=k;r<=n;l++,r++){
f[l][r]=pre[r]-pre[l-1];
f[l][r]=max(f[l][r],mx[l+1][r]-pre[l-1]);
f[l][r]=max(f[l][r],pre[r]-mn[l][r-1]);
mx[l][r]=max(mx[l+1][r],pre[l-1]-f[l][r]);
mn[l][r]=min(mn[l][r-1],pre[r]+f[l][r]);
}
printf("%d\n",(f[1][n]+pre[n])/2);
}
return 0;
}
思路2:參考另一位大佬的,我覺得比上面一種方法好,但是容易超時,自己手寫能快一倍的速度。。。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e3+5,M=1e6+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a) memset(a,0,sizeof a)
#define lx x<<1
#define rx x<<1|1
#define reg register
#define PII pair<int,int>
#define fi first
#define se second
int a[N],dp[N][N],f[N][N],g[N][N];
inline void read(int &x){
x=0;int w=1;
char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') w=-1;ch=getchar();}
for(;ch>='0'&&ch<='9';ch=getchar())
x=(x<<3)+(x<<1)+(ch&15);
x*=w;
}
int main(){
int t;
read(t);
while(t--){
int n;
read(n);
for(reg int i=1;i<=n;i++){
read(a[i]),dp[i][i]=f[i][i]=g[i][i]=a[i];
a[i]+=a[i-1];
}
for(reg int k=2;k<=n;k++)
for(reg int l=1,r=k;r<=n;l++,r++){
int tmp=0;
if(f[l+1][r]<g[l][r-1]) tmp=f[l+1][r];
else tmp=g[l][r-1];
if(tmp>0) tmp=0;
dp[l][r]=a[r]-a[l-1]-tmp;
if(f[l+1][r]>dp[l][r]) f[l][r]=dp[l][r];
else f[l][r]=f[l+1][r];
if(g[l][r-1]>dp[l][r]) g[l][r]=dp[l][r];
else g[l][r]=g[l][r-1];
}
printf("%d\n",dp[1][n]);
}
return 0;
}