Atcoder agc032D

这题有个一眼的dp式,设 F [ i ] [ j ] F[i][j] F[i][j]表示将原序列中权值在 [ i , j ] [i,j] [i,j]中的点排序的最小代价,转移直接枚举一下左右端点。
很可惜是假的,可以被5 1 2 3 4 7 8 9 10 6这种数据卡掉。
考虑靠谱的做法,每次旋转只会改变一个数和其他数的相对位置,所以如果改变一个数的相对位置,一定会改到最终的位置。
那么设 F [ i ] [ j ] F[i][j] F[i][j]表示将原序列前 i i i个数中不超过 j j j的数字排序的最小代价。
考虑最大值 j j j,若它在最后就不用动,否则有两种选择,第一种是直接花 a a a的代价放到最后,转移到 F [ i ] [ j − 1 ] F[i][j-1] F[i][j1],否则后面的数都必须转到前面来,每个数需要 b b b的代价,转移到 F [ p j − 1 − 1 ] [ j ] F[p^{-1}_j-1][j] F[pj11][j]

#include 
 
using namespace std;
 
typedef long long ll;
 
int num[5005],pos[5005],cnt[5005];
ll f[5005][5005];
 
int main() {
  memset(f,0x3f,sizeof(f));
  int n,a,b;
  scanf("%d%d%d",&n,&a,&b);
  for(int i=1;i<=n;i++) {
  	scanf("%d",&num[i]);
  	pos[num[i]]=i;
  }
  for(int i=0;i<=n;i++) f[i][0]=f[0][i]=0;
  for(int i=1;i<=n;i++) {
  	for(int j=num[i]+1;j<=n;j++)
  	  if (pos[j]<i) cnt[j]++;
    for(int j=1;j<=n;j++)
      if (pos[j]<=i) {
      	if (!cnt[j]) f[i][j]=f[i][j-1];
      	else f[i][j]=min(f[i][j-1]+a,f[pos[j]-1][j]+(ll)b*cnt[j]);
	  }
      else f[i][j]=f[i][j-1];
  }
  printf("%lld\n",f[n][n]);
  return 0;
}

你可能感兴趣的:(集训队作业,atcoder,动态规划)