题目链接:https://www.patest.cn/contests/gplt/L3-001
*这道题许多大佬用DFS做出来了,Orz!
*为了得到最小序列,不妨按升序排序,且删除值大于m的硬币!
*做法: F[i][j] =1定义: 必须拿第i块硬币,后面的硬币随便你取,能组合j元!不能即为0!
G[i][j] =1定义: 可拿或不拿第i块硬币,后面的硬币随便你取,能组合j元!不能即为0!
*j<=100,i<=1e4,内存O(1e6);
*如果G[0][m]==0,表明所有硬币无论怎么取,不能组合m元!"No Solution"!
G[0][m]==1,绝对有解!
*后面我们仅需要dfs出最小序列了,dfs(rt,x)作用:遍历[rt,temp)出组合成x元的最小序列;
函数内考虑两个选择: 能取rt,即F[rt][x]==1,它绝对是当前最小序列的首元素!dfs(rt+1,x-a[rt])!
不能取rt,即F[rt][x]==0,那跳过,继续dfs(rt+1,x)!
#include <iostream>
#include <stdio.h>
#include <algorithm>
using namespace std;
int F[10010][105]={0};// Fij即一定要拿第i块硬币,其它后面硬币随便拿能够组成j元
int G[10010][105]={0};// Gij即可不拿第i块硬币,其它后面硬币随便拿能够组成i元
//F[i][j]=G[i+1][j-a[i]]
//G[i][j]=G[i+1][j]|F[i][j]
int a[10010];
int first=1;
void dfs(int rt,int m){
if(m==0)return;
if(F[rt][m]){
if(first){first=0;}
else printf(" ");
printf("%d",a[rt]);
dfs(rt+1,m-a[rt]);
return ;
}
dfs(rt+1,m);
}
int main(){
int N,M;
scanf("%d%d",&N,&M);
int temp=0;
while(N--){
int b;
scanf("%d",&b);
if(b<=M)a[temp++]=b;
}
sort(a,a+temp);
for(int i=0;i<=temp;++i)
G[i][0]=1;
for(int i=temp-1;i>=0;--i){
for(int j=0;j+a[i]<=M;++j)
F[i][j+a[i]]=G[i+1][j];
for(int j=1;j<=M;++j)
G[i][j]=G[i+1][j]|F[i][j];
}
if(G[0][M]==0)printf("No Solution");
else dfs(0,M);
printf("\n");
return 0;
}