这道题要求我们在差最小的情况下反转次数最少。我们用dp[i][j]表示选取前i个股票,差值为j的最小反转。因为差最小是优先条件,所以我们完全可以找到最接近某一个值的点,取其最小反转次数。
那么dp[i][j] = min(dp[i-1][j+a[i]],dp[i-1][j-a[i]]+1),其中a[i]表示第i块骨牌上下点数之差(不是绝对值!!),所以你在取相反数的时候相当于反了过来。至于怎么解决负数的问题,好办,数据波动不超过5000,所以只要设置一个中间值(比如6000),把它设为开始开始DP,最后找一个最接近的即可。
看一下代码。
#include<cstdio> #include<algorithm> #include<cstring> #include<iostream> #include<cmath> #include<set> #include<queue> #define rep(i,a,n) for(int i = a;i <= n;i++) #define per(i,n,a) for(int i = n;i >= a;i--) #define enter putchar(' ') using namespace std; typedef long long ll; const int M = 20005; const int INF = 1000000009; int read() { int ans = 0,op = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') op = -1; ch = getchar(); } while(ch >= '0' && ch <= '9') { ans *= 10; ans += ch - '0'; ch = getchar(); } return ans * op; } int n,k,a[M],dp[1005][12005],x,y,minn = INF,ans; int main() { n = read(); rep(i,1,n) x = read(),y = read(),a[i] = x - y; rep(i,0,n) rep(j,0,12000) dp[i][j] = INF; dp[0][6000] = 0; rep(i,1,n) { rep(j,1000,11000) { dp[i][j] = min(dp[i][j],dp[i-1][j+a[i]]); dp[i][j] = min(dp[i][j],dp[i-1][j-a[i]]+1); } } rep(j,1000,11000) { if(dp[n][j] == INF) continue; if(abs(6000 - j) < minn) minn = abs(6000-j),ans = dp[n][j]; } printf("%d ",ans); return 0; }