A 魔力屏障

发布时间 2023-11-17 13:00:54作者: wscqwq

A 魔力屏障

我们考虑设 \(f[i][j][k]\) 表示击破区间 \([i,j]\) 后,剩余魔力 \(k\),最少需要多少能量。

初始状态:对于所有区间为 \([i,i]\),击破后剩余 \(a[i]/2\),最少需要 \(a[i]\)。(如果击破能量超过 \(a[i]/2\),可以发现不是最优的,因为穿过之后会减半,所以尽量还是下次发功)。

转移,考虑先击破 \([mid,j]\),再击破 \([i,mid-1]\),那么剩余的能量会相加(如果顺序反一下,可以发现相当于一次性击破整个,待会儿考虑),即 \(f[i][j][k]=\min(f[i][mid-1][k-p]+f[mid][j][p])\)\(p\) 的取值需要满足 \(\ge a_j/2\),因为至少要 \(a_j\) 才能击穿到达 \(a_j/2\) 这个),然后再考虑 \(f[i][j][\max(p,a_r)/2(保证可以击穿)]=f[i][j-1][p]+\max(0,a_j-p)\)

发现 \(k\) 这一维取到 \(\max a\) 即可,这个原因和初始状态设定的原因是一样的。

#include<iostream>
#include<cstring>
using namespace std;
#define Ed for(int i=h[x];~i;i=ne[i])
#define Ls(i,l,r) for(int i=l;i<r;++i)
#define Rs(i,l,r) for(int i=l;i>r;--i)
#define Le(i,l,r) for(int i=l;i<=r;++i)
#define Re(i,l,r) for(int i=l;i>=r;--i)
#define L(i,l) for(int i=0;i<l;++i)
#define E(i,l) for(int i=1;i<=l;++i)
#define W(t) while(t--)
#define Wh while

const int N=75,M=160;
int n,a[N],f[N][N][M],m;
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	memset(f,0x3f,sizeof f);
	cin>>n;
	E(i, n)cin>>a[i],m=max(m,a[i]);
	E(i, n){
		f[i][i][a[i]>>1]=a[i];
		Le(j, (a[i]>>1)+1, m)f[i][i][j]=j<<1;//可略去
	}
	Le(len, 2, n)
		for(int l=1;l+len-1<=n;++l){
			int r=l+len-1;
			Le(mid, l+1, r){
				Le(p, a[r]>>1, m)
					Le(k, a[r]>>1, p)
						f[l][r][p]=min(f[l][r][p],f[l][mid-1][p-k]+f[mid][r][k]);
			}
			L(p, m+1){
				int nxt=max(p,a[r])>>1;
				f[l][r][nxt]=min(f[l][r][nxt],f[l][r-1][p]+max(0,a[r]-p));
			}
		}
	E(i, n){
		int ret=1e9;
		Le(k, a[i]>>1, m)
			ret=min(ret,f[1][i][k]);
		cout<<ret<<' ';
	}
	return 0;
}