题意: 给了一个n,k,s,和一个数组,求使用小于s次的交换使得前k个整数的和尽可能的小,交换指的的是相邻的两个数交换,
首先考虑 如果最小的k个数全部再最后面,那么至少要花费 ( n - k + 1 + n)*k/2 - (( k + 1 ) * k / 2) 这么多下才能把这k个数搬运到最前面,也就是说如果s他大于等于这个数 那么答案一定是最小的那k个数,
否则进行下面的做法
假设前k个数 在最初的 那个数组中的下标分别是L1<L2<...<Lk,肯定被选中的那k个数他们的相对位置肯定是不变得,如果变了会增加交换的次数
那么我们就假设就是最初给的那个数组的下标L1 L2 L3 L4..Lk 那么所要的交换此时就是 T=L1-1+L2-2+...Lk-k我们知道这个要小于等于S
于是得到 L1+L2+...+Lk<=S+(1+k)*k/2,采用dp[i][j][p]表示 前i个数 选了j个他们的下标和为p的最小值,dp[i][j][p]=min(dp[i-1][j][p],dp[i-1][j-1][p-i]+A[i])
数据太大 采用滚动数组,
#include <iostream> #include <algorithm> #include <cstdio> #include <vector> #include <string.h> using namespace std; const int maxn=155; const int INF=2147483647; int A[maxn]; int dp[2][maxn][maxn*maxn*2]; int main() { int n,k,s; scanf("%d%d%d",&n,&k,&s); for(int i=1; i<=n; i++) scanf("%d",&A[i]); if(k==n) { int sum=0; for(int i=1; i<=n; i++) sum+=A[i]; printf("%d\n",sum);return 0; } if( s >= ( n - k + 1 + n)*k/2 - (( k + 1 ) * k / 2 ) ) { sort(A+1,A+1+n); int sum=0; for(int i=1; i<=k; i++) sum+=A[i]; printf("%d\n",sum);return 0; } int S=s+(k+1)*k/2; int cur=0,per=1; for(int i=1; i<=k; i++) for(int j=0; j<=S; j++)dp[0][i][j]=INF; for(int i=1; i<=n; i++) { cur^=1; per^=1; for(int j=1; j<=k; j++) { for(int a=0; a<=S; a++) { dp[cur][j][a]=dp[per][j][a]; if(a>=i&&dp[per][j-1][a-i]!=INF){ dp[cur][j][a]=min(dp[per][j-1][a-i]+A[i],dp[cur][j][a]); } } } } int ans=INF; for(int i=0; i<=S; i++) ans=min(dp[cur][k][i],ans); printf("%d\n",ans); return 0; }
时间: 2024-10-16 10:47:54