天平(balance.in/balance.out)
物理老师YJ有一个长杆天平,天平的两臂长均为15,将长杆看作x轴,则平衡点在0位置处,负数位置在左臂上,正数位置在右臂上。长杆上有n个位置有挂钩可以挂秤砣。YJ有m个秤砣,质量分别为gi,每个挂钩可以不挂也可以挂任意个秤砣。YJ想要知道,在使用所有秤砣的条件下,有多少种不同的挂秤砣的方案,可以使得天平平衡?问题太过复杂,仅凭物理知识难以解决,所以请你来帮助他。
天平的平衡条件是所有秤砣的位置质量之和为0。例如有质量为2,3,4的秤砣分别挂在-3,-2,3位置处,则2(-3) + 3*(-2) + 4*3 == 0,天平是平衡的。
【输入格式】
第一行两个数n,m。表示挂钩的数目和秤砣的数目。
第二行n个不同且递增的数,第i个数表示第i个挂钩的位置,数的范围在[-15,15]内。
第三行m个不同且递增的数,第i个数表示第i个秤砣的质量,数的范围在[1,25]内。
【输出格式】
一个整数,代表能使得天平平衡的方案数。
【输入样例】
2 4
-2 3
3 4 5 8
【输出样例】
2
【样例解释】
方案1: (-2)*(3+4+5) + 3*8 = 0
方案2: (-2)(4+8) + 3(3+5) = 0
【数据规模】
10% 数据满足2≤n,m≤4。
100% 数据满足2≤n,m≤20。
【解法】
简单的DP。考虑天平的状态可以用秤砣质量距离来表示,而题目情况下,天平的全部秤砣质量距离在(-7500,7500)(20*25*15 = 7500)以内。故可以以此状态来dp。
类似背包问题,把每一个秤砣挂在不同地方,对应成n*m个物品,转移方程f[i][j]+=f[i-1][j-dis[k]*mg[i]] (i=1..n,j=0..15000,k=1..m)。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<set>
#include<queue>
#include<algorithm>
#include<vector>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<stack>
#define INF 2100000000
#define LL long long
#define clr(x) memset(x,0,sizeof(x))
#define ms(a,x) memset(x,a,sizeof(x))
#ifdef win32
#define AUTO "%I64d"
#else
#define AUTO "%lld"
#endif
using namespace std;
const int maxn = 25;
int n,m;
LL c[maxn],t[maxn];
LL dp[maxn][15005];
template <class T> inline void read(T &x) {
int flag = 1; x = 0;
char ch = getchar();
while(ch < '0' || ch > '9') { if(ch == '-') flag = -1; ch = getchar(); }
while(ch >= '0' && ch <= '9') { x = (x<<1)+(x<<3)+ch-'0'; ch = getchar(); }
x *= flag;
}
int main() {
freopen("balance.in","r",stdin);
freopen("balance.out","w",stdout);
read(n); read(m);
for(int i = 1; i <= n; i++) read(t[i]);
for(int i = 1; i <= m; i++) read(c[i]);
dp[0][7500] = 1;
for(int i = 1; i <= m; i++)
for(int k = 1; k <= n; k++)
for(int j = 0; j <= 15000; j++) {
if(j-t[k]*c[i] < 0 || j-t[k]*c[i] > 15000) continue;
dp[i][j] += dp[i-1][j-t[k]*c[i]];
}
cout << dp[m][7500] << endl;
return 0;
}