CF590D Top Secret Task
题目描述
有n个数,有s次操作,每次操作可以交换两个相邻的数,现在要求通过s次操作,使前k个数的和最小,输出这个最小的和
n,k,s(1<=k<=n<=150,1<=s<=1e9)
Solution
容易发现虽然S很大
但只需要n*(n-1)/2次操作就可以把这个序列排有序
考虑S比n*(n-1)/2次操作小的时候
很显然,如果前J个数已经排好,则第i个数要放就一定换到第j+1位
由此DP就行,滚动数组优化空间
#include<bits/stdc++.h> using namespace std; inline int read() { int f = 1,x = 0; char ch; do { ch = getchar(); if(ch == '-') f = -1; }while(ch < '0'||ch > '9'); do { x = (x<<3) + (x<<1) + ch - '0'; ch = getchar(); }while(ch >= '0'&&ch <= '9'); return f*x; } int n,K,s; int a[150 + 10]; long long dp[2][150 + 10][150*150 + 10]; int main() { while(~scanf("%d%d%d",&n,&K,&s)) { for(int i=1;i<=n;i++) a[i] = read(); if(s >= n*(n-1)/2) { sort(a+1,a+n+1); long long sum = 0; for(int i=1;i<=K;i++) sum += a[i]; cout << sum << endl; continue; } memset(dp,127,sizeof(dp)); dp[0][0][0] = 0; for(int i=1;i<=n;i++) { int cur = i&1; dp[cur][0][0] = 0; for(int j=1;j<=i;j++) { for(int k=0;k<=i*j;k++) { dp[cur][j][k] = dp[cur^1][j][k]; if(k >= (i-j)) dp[cur][j][k] = min(dp[cur][j][k],dp[cur^1][j-1][k-(i-j)] + a[i]); // cout << i << " " << j << " " << k << " " << dp[cur][j][k] << endl; } } } long long ans = 1<<30; for(int i=0;i<=s;i++) ans = min(ans , dp[n&1][K][i]); cout << ans << endl; } }