Codeforces 590D Top Secret Task

来源:互联网 发布:更改照片的软件 编辑:程序博客网 时间:2024/04/30 01:12

CF 590D

题目大意:

给出一个长度为N的序列,同时你有S次机会交换任意两个相邻位置的数,希望前K个数的和最小。

错误思路。。

  • 很容易想到状态为:f[i][j]:前i个数交换了j次的最小和。
  • 然后就在f[k][j]: j[0,S] 中找到一个最小的值就是答案了。

    想法总是很好的。但是转移怎么办???
    按照正常的想法可以这样:

f[i][j]=min{f[i][j],f[i][jk]+(a[i+k][i])}

这个方程表示如果第i个数可以被交换,则显然交换j次可以最远更新到a[i+j],然后交换a[i+k]与a[i];
But!!

Dp 是不能有后效性的!!

而当我们交换若干次后,a[i+k]可能早已不是以前a[]中的数值,即它可能已经被交换过了。所以,这个方程是错的。

正解

状态不变,但是方程要变为f[i+1][j+k-(i+1)]=min(f[i+1][j+k-(i+1)],f[i][j]+a[k]);

  1. 由当前的f[i][j]来更新f[i+1][j+k-(i+1)]【小->大】
  2. 为了没有后效性,一层循环由小到大枚举,一层由大到小枚举。

Attention:
题目中S的范围很大,10^9但是并不需要这么多,事实上对于长度为n的序列,最多只能交换n*(n-1)次,这个很好理解,也可以打表确定一下;

Code:

#include <stdio.h>#include <string.h>#define INF 99999999;int n,k,s,ans;int a[151],f[151][150*150];int min(int a,int b){return a<b?a:b;}int main(){    scanf("%d%d%d",&n,&k,&s);    for(int i=1;i<=n;i++)    {        scanf("%d",&a[i]);    }    memset(f,0x3f,sizeof(f));    f[0][0]=0;    for(int k=1;k<=n;k++)    {        for(int i=k-1;i>=0;i--)        {            for(int j=0;j<=k*i;j++)            {                if(f[i][j]!=0x3f3f3f3f)                {                    f[i+1][j+k-(i+1)]=min(f[i+1][j+k-(i+1)],f[i][j]+a[k]);                }            }        }    }    memset(&ans,127,sizeof(int));    for(int j=0;j<=min(s,(n*(n)));j++)    {        ans=min(ans,f[k][j]);    }    printf("%d\n",ans);    return 0;}

P.S. 自动忽略ans的赋值就好啦。。

1 0
原创粉丝点击