这道题很有收获。
我自己想的时候有想到这是一个背包,但是写的实现非常麻烦,还是错的。
我想的是在背包的过程中尽量靠近0,但是这样显然有后效性
而且我思考背包的方式不对。要考虑当前物品有哪几种可能,然后
比如这道题就是交换或者不交换。先用二维的思考方式,滚动数组
只是后面的优化。
后来看了题解,很有收获
(1)如何防止下标为负数。可以设一个基准数base,然后每次涉及到重量的
都加上base,这个base的值是负数的最小值。同时注意数组空间就要开大一些。
这样就避免了负数下标
(2)先做后统计答案。f[i][j]是前i个骨牌构成和为j的最小交换次数。
这个答案涉及到最小的数以及次数,一个作维度,一个作值,这样最后
扫遍数组就可以得出答案。并不是说最后一定要输出f[n][m]什么的。
这样的思路非常棒。
#include<cstdio>
#include<cstring>
#include<algorithm>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;const int MAXN = 1123;
const int base = 5000;
int a[MAXN], b[MAXN], f[MAXN][base*2+10], n;int main()
{scanf("%d", &n);_for(i, 1, n) scanf("%d%d", &a[i], &b[i]);memset(f, 0x3f, sizeof(f));f[0][0+base] = 0;_for(i, 1, n)_for(j, -base, base){int t = a[i] - b[i];f[i][j+base] = min(f[i-1][j-t+base], f[i-1][j+t+base] + 1); }_for(j, 0, base){int t = min(f[n][j+base], f[n][-j+base]);if(t <= 1000){printf("%d\n", t);break;}} return 0;
}
滚动数组优化版本(时间甚至还少了。看来空间减少时间也会减少一些。)
#include<cstdio>
#include<cstring>
#include<algorithm>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;const int MAXN = 1123;
const int base = 5000;
int a[MAXN], b[MAXN], f[2][base*2+10], n;int main()
{scanf("%d", &n);_for(i, 1, n) scanf("%d%d", &a[i], &b[i]);memset(f, 0x3f, sizeof(f));int p = 1;f[p][0+base] = 0;_for(i, 1, n){p ^= 1;_for(j, -base, base){int t = a[i] - b[i];f[p][j+base] = min(f[p^1][j-t+base], f[p^1][j+t+base] + 1); }}_for(j, 0, base){int t = min(f[p][j+base], f[p][-j+base]);if(t <= 1000){printf("%d\n", t);break;}} return 0;
}