中文题
想法:
观察到每次交换只是把上下两个数交换,故前i个骨牌上下两行数的总和是不变的,所以我们只需记录其中一行数字的和就可以知道差值了。这样状态就好表示了。
f[i][j]表示前i个数字,第一行的数字和是j时,最小的交换次数。初始值所有f[i][j]都是无穷大,f[1][a[1]]=0,f[1][b[1]]=1。(a[]和b[]分别表示第一行和第二行的数字)
转移时,枚举每一个可能的和,共有6*n个,考虑当前一个交不交换即可
最后去枚举第一行的和去找满足题意(差值最小)的最小交换次数
#include <algorithm> #include <string> #include <string.h> #include <vector> #include <map> #include <stack> #include <set> #include <queue> #include <math.h> #include <cstdio> #include <iomanip> #include <time.h> #include <bitset> #include <cmath> #include <sstream> #include <iostream> #include <cstring> #define LL long long #define ls nod<<1 #define rs (nod<<1)+1 #define pii pair<int,int> #define mp make_pair #define pb push_back #define INF 0x3f3f3f3f const double eps = 1e-10; const int maxn = 1e3 + 10; const LL mod = 1e9 + 7; int sgn(double a){return a < -eps ? -1 : a < eps ? 0 : 1;} using namespace std; int a[maxn],b[maxn]; int f[maxn][6*maxn]; int main() { ios::sync_with_stdio(false); int n; cin >> n; int tot = 0; for (int i = 1;i <= n;i++) { cin >> a[i] >> b[i]; tot += (a[i] + b[i]); } for (int i = 0;i <= n;i++) { for (int j = 0;j <= 6*n;j++) f[i][j] = INF; } f[1][b[1]] = 1; f[1][a[1]] = 0; for (int i = 2;i <= n;i++) { for (int j = 0;j <= 6*n;j++) { if (j >= a[i]) f[i][j] = min(f[i][j],f[i-1][j-a[i]]); if (j >= b[i]) f[i][j] = min(f[i][j],f[i-1][j-b[i]]+1); } } int Minn = INF; int ans = INF; for (int i = 0;i <= tot;i++) { if (f[n][i] != INF) { if (abs(i-(tot-i)) < Minn) { Minn = abs(i-(tot-i)); ans = f[n][i]; } else if (abs(i-(tot-i)) == Minn) ans = min(ans,f[n][i]); } } cout << ans << endl; return 0; }