题面传送门
这是一道区间dp题。
这道题和P1220 关路灯特别相似,因为他们的状态转移方程都类似于
f [ l ] [ r ] [ 0 ] = m i n ( f [ l + 1 ] [ r ] [ 0 ] + 改 变 量 , f [ l + 1 ] [ r ] [ 1 ] + 改 变 量 ) f[l][r][0]=min( f[ l+1 ][ r ][ 0 ] +改变量 , f[ l+1 ][ r ][ 1 ] +改变量) f[l][r][0]=min(f[l+1][r][0]+改变量,f[l+1][r][1]+改变量)
f [ l ] [ r ] [ 1 ] = m i n ( f [ l ] [ r − 1 ] [ 0 ] + 改 变 量 , f [ l ] [ r − 1 ] [ 1 ] + 改 变 量 ) f[l][r][1]=min( f[ l ][ r-1 ][ 0 ] +改变量 , f[ l ][ r-1 ][ 1 ] +改变量) f[l][r][1]=min(f[l][r−1][0]+改变量,f[l][r−1][1]+改变量)
f [ i ] [ j ] [ 0 ] f[i][j][0] f[i][j][0]和 f [ i ] [ j ] [ 1 ] f[i][j][1] f[i][j][1]表示当前已经套完了 i i i到 j j j的牛,然后用第三维表示在左边还是在右边。
我们把 n n n个点拆成 n + 1 n+1 n+1个点,找到 0 0 0点,设为 c c c,令 f [ c ] [ c ] [ 0 ] = f [ c ] [ c ] [ 1 ] = 0 f[c][c][0]=f[c][c][1]=0 f[c][c][0]=f[c][c][1]=0,其他的都为 0 x 3 f 0x3f 0x3f。
我们下一步可以选择去 i − 1 i-1 i−1,也就是下一步到 f [ i − 1 ] [ j ] f[i-1][j] f[i−1][j],或者选择去 j + 1 j+1 j+1,到 f [ i ] [ j + 1 ] f[i][j+1] f[i][j+1]。
对于每一个当前的状态, a [ j ] a[j] a[j]可能在左边或者右边,也就是 f [ i ] [ j ] [ 1 ] f[i][j][1] f[i][j][1]或者 f [ i ] [ j ] [ 0 ] f[i][j][0] f[i][j][0],每一个又有两种转移方案,于是我们可以把两个状态一起两重暴力进行转移。
其中 破 坏 量 = 距 离 ∗ 未 套 住 的 数 量 破坏量=距离*未套住的数量 破坏量=距离∗未套住的数量
复杂度为 O ( n 2 ) O(n^2) O(n2)。
#include
#include
#include
#include //养成好习惯,不用万能头
#define maxn 1039
using namespace std;
int n,f[maxn][maxn][2],a[maxn],c=0;
//inline int max(int x,int y){return x>y?x:y;}
inline int min(int x,int y){return x<y?x:y;} //养成好习惯,手写min函数
inline int js(int x,int y,int l,int r){return (a[y]-a[x])*(n-r+l);} //计算破坏量
int main(){
memset(f,0x3f,sizeof(f));
register int i,j;
scanf("%d",&n);
for(i=1;i<=n;i++)
scanf("%d",a+i);
n++;
a[n]=0; //把0塞在数组的最末端
sort(a+1,a+n+1); //排序
for(i=1;i<=n;i++) //找到0点
if(!a[i]) c=i;
f[c][c][0]=f[c][c][1]=0;
for(j=c;j<=n;j++) //区间dp
for(i=j-1;i>=1;i--){
f[i][j][0]=min(f[i+1][j][0]+js(i,i+1,i,j),f[i+1][j][1]+js(i,j,i,j));//套刚才推出的状态转移方程
f[i][j][1]=min(f[i][j-1][0]+js(i,j,i-1,j-1),f[i][j-1][1]+js(j-1,j,i-1,j-1));
}
printf("%d",min(f[1][n][0],f[1][n][1]));
return 0; //完美撒花
}