灵感来自题下面那个图。
一开始想用dp[i][j]表示前i个coin得到j时的最少硬币数,但是后来想了想不满足无后效性。于是转化为dp[i][j][k]表示前i种硬币,分别得到x为j,y为k时的最少硬币数。这样答案就是dp[i][j][k],i==m,j*j+k*k==S*S的时候。这样对于每个coin,就那个图而言,枚举它每个终点,然后dp即可。一开始不知道哪写错了,还加了排序,一直没过样例,后来想了想发现数组的第一维没有必要写,排序也没必要写。然后就过了。。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#define ll long long
#define MAXN 305
#define INF 2139062143
using namespace std;
int dp[MAXN][MAXN];
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
int m,S;
scanf("%d%d",&m,&S);
int x[45],y[45];
memset(dp,0x7f,sizeof(dp));
for(int i=0; i<m; ++i)
scanf("%d%d",&x[i],&y[i]);
dp[0][0]=0;
for(int i=0; i<m; ++i)
{
for(int j=0; j<=S; ++j)
for(int k=0; k<=S; ++k)
if(j>=x[i]&&k>=y[i])
dp[j][k]=min(dp[j][k],dp[j-x[i]][k-y[i]]+1);
}
int ans=INF;
for(int i=0; i<=S; ++i)
for(int j=0; j<=S; ++j)
if(i*i+j*j==S*S&&dp[i][j])
ans=min(ans,dp[i][j]);
if(ans!=INF) printf("%d\n",ans);
else puts("not possible");
}
return 0;
}