bzoj 4244 括号序列dp
将各种情况绕环等看作括号序列,括号内的区域上下都需要累加答案,左右也是
f[i][j] 代表 前i个车站已经处理完的有j个左括号的最小权值
我们可以发现,更新的来源来自于 i-1, 和 i
将上 描述为L1,L2, 下描述为R1,R2,所以可以通过括号内的沿伸以及左右括号的答案更新状态
具体代码如下
#include<bits/stdc++.h> #define rep(i,x,y) for(register int i=x;i<=y;i++) #define dec(i,x,y) for(register int i=x;i>=y;i--) using namespace std; inline int read(){ int x=0,f=1;char ch=getchar(); while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();} while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} return x*f;} namespace zkc{ const int N=3050; int n,T,L1[N],L2[N],R1[N],R2[N],f[N][N]; inline void work(){ n=read(),T=read(); rep(i,1,n) R1[i]=read(),L1[i]=read(),L2[i]=read(),R2[i]=read(); memset(f,0x3f,sizeof f);f[0][0]=0; rep(i,1,n){ //from i-1 zhuan'yi rep(j,0,n){ if(j){//来自于i-1的转移 f[i][j]=min(f[i][j],f[i-1][j-1]+(j-1)*2*T+L1[i]+L2[i]); //第一个转移需要保证j的原因是其由左边j-1转移而来(
f[i][j]=min(f[i][j],f[i-1][j]+j*2*T+L2[i]+R2[i]);}
//由于此条转移走的是下路,也就是已经到了下班状态,至少有一次向下的左括号还没有被匹配 //下班路上取邮戳
f[i][j]=min(f[i][j],f[i-1][j]+j*2*T+L1[i]+R1[i]);
//上班路上取邮戳 f[i][j]=min(f[i][j],f[i-1][j+1]+(j+1)*2*T+R1[i]+R2[i]); //匹配右括号
}
//前四种转移是强行为了更新状态而走邮戳站的,底下的两种是为了更新自己此节点的反复转移的情况,即在同一节点处多次更新 rep(j,1,n) f[i][j]=min(f[i][j],f[i][j-1]+L1[i]+L2[i]); //先更新小的j-1再更新大的j+1
dec(j,n-1,0) f[i][j]=min(f[i][j],f[i][j+1]+R1[i]+R2[i]);
} printf("%d\n",f[n][0]+(n+1)*T);return; } } int main(){ zkc::work(); return 0; }
完结撒花
不过话说回来,其实还有许多不太明白的转移顺序问题,日后慢慢理解哈