题目大意:给出N个硬币,每个硬币有对应的X和Y,ecoin的值= Sqrt(X * X + Y * Y).问这些硬币能否得到给定的ecoin。能的话输出最小的组成数目。不能酒输出Not possible。
解题思路:这里的X和Y并不是指定是一个硬币的X或Y,可以是多个硬币来组成这个ecoin的值。例如 3 0 和 0 4 是可以组成ecoin5的。
所以这里用dp【x】【y】表示达到X和Y这个状态时,最少要几个coin。
dp【x】【y】 = Min (dp【x - v【i】.x】dp【y - v【i】.y] + 1) 【i >= 1 && i <= m】 , m 代表给定的硬币的个数。
对于给定的ecoin,就要找出满足 X * X + Y * Y = ecoin的所有的X。然后用记忆化搜索一下答案,取最小值。这里有找不到的情况,也有没有找的情况。这两个情况要区分开。
代码:
#include <cstdio>
#include <cmath>
#include <cstring>
const int N = 45;
const int maxn = 305;
const int INF = 0x3fffffff;
int value[N][2];
int dp[maxn][maxn];
int n, s;
int Min (const int a, const int b) { return a < b ? a: b; }
void init () {
for (int i = 0; i <= s; i++)
for (int j = 0; j <= s; j++)
dp[i][j] = INF; //代表没找过的
for (int i = 0; i < n; i++)
dp[value[i][0]][value[i][1]] = 1;
/* printf ("%d\n", s);
for (int i = 0; i <= s; i++) {
for (int j = 0; j <= s; j++)
printf ("%d ", dp[i][j]);
printf ("\n");
}*/
}
int DP (int x, int y) {
int& ans = dp[x][y];
if (ans != INF)
return ans;
int x1, y1;
for (int i = 0; i < n; i++) {
x1 = value[i][0];
y1 = value[i][1];
if (x >= x1 && y >= y1)
ans = Min (ans, DP(x - x1, y - y1) + 1);
}
if (ans == INF)
ans = INF + 1; //代表这些硬币不论怎么组成都不能得到这样的X和Y
return ans;
}
int main () {
int t;
scanf ("%d", &t);
int ans;
while (t--) {
scanf ("%d%d", &n, &s);
for (int i = 0; i < n; i++)
scanf ("%d%d", &value[i][0], &value[i][1]);
init ();
ans = INF;
int y;
for (int x = 0; x <= s; x++) {
y = (int) sqrt (s * s - x * x);
if (y * y + x * x == s * s)
ans = Min (ans, DP(x, y));
}
if (ans == INF)
printf ("not possible\n");
else
printf ("%d\n", ans);
}
return 0;
}