SDUT 2022 Summer Individual Contest - 3 (补题)
概述:一个长度为n的数组,选k个数进行两两相减,使得它们的绝对值的和最大为F(k),求k=2,3,....n是F(k)的值
分析:k=2时最大值一定时max-min
k=3时,里面的已经有max和min,第三个数为x,则有F(3)=F(2)+(max-x)+(x-min)=F(2)+F(2)
k=4时,里面的已经有max,min和x,第四个数为y,则有F(4)=F(3)+(max-y)+(y-min)+|y-x|=F(3)+F(2)+|y-x|,所以此时的x与y应该为次大与次小
所以依次取里面最大和最小值即为最优解,借鉴滨神优质代码如下
(滨神代码tql)
#include <bits/stdc++.h> #define endl '\n' #define x first #define y second #define PI acos(-1) #define int long long #define SugarT ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); using namespace std; const int N=3e5+10; const int INF=0x3f3f3f3f3f3f3f3f; const int mod=1e9+7; const double eps = 1e-6; int q[N]; int res[N]; int c=2; void solve() { int n; cin >> n; for(int i=1;i<=n;i++)cin >> q[i]; sort(q+1,q+1+n); for(int i=1,j=n;i<j;i++,j--) res[i]=res[i-1]+q[j]-q[i]; int ans=0; for(int i=2;i<=n;i++) { ans+=res[i>>1]; cout << ans << " "; } } signed main() { SugarT int T=1; //cin >> T; while(T--) solve(); return 0; }
概述:题目目标是得到一个数N,为此可以进行两种操作,加上10x (x>=0)或是减去10x (x>=0),求操作次数的最小值。
分析:贪心不能解决问题的一律使用dp大法
两种方法——递加或借位递减,用dp[i][0],dp[i][1]表示第i位上运用第一种操作和第二种操作
#include <bits/stdc++.h> #define endl '\n' #define x first #define y second #define PI acos(-1) #define int long long #define SugarT ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); using namespace std; const int N=1e5+10; const int INF=0x3f3f3f3f3f3f3f3f; const int mod=1e9+7; const double eps = 1e-6; void solve() { string s; cin >> s; int a=s[0]-'0';//第一位运用累加就是这个数字本身 int b=10-a+1;//第二位运用累减就是前面拿1,以1当10再减去后面拿多的 int ans; for(int i=1;i<s.size();i++) { int aa=a,bb=b; int k=10-(s[i]-'0'); a=min(aa,bb)+s[i]-'0';//累加法直接加就可以 b=min(1+k+aa,k+(bb-1));//累减法需要用前面的一位借出一个1来对后面进行操作,手动模拟一遍即可 } ans=min(a,b);//取两种方法的最小值 cout << ans << endl; } signed main() { SugarT int T=1; //cin >> T; while(T--) solve(); return 0; }