数据选取
Time Limit:1000MS Memory Limit:32768K
Description:
欢迎进入“挂羊头,卖狗肉”专柜~~下面继续~~给定1个数m和n个数{a1,a2,...,an},那么请问,最少需要从这n个数中选出多少个,使这些数之和恰好等于m?
Input:
首先一个整数cas,表示接下来由cas个数据每组数据第一行两个整数m,n(1<=m<=100,1<=n<=10) 第二行包含n个整数,表示数组a, 每个整数在[1,10]之间Output:
如果有满足条件的选法,输出最少需要选出的个数.否则,输出-1 每组输出占一行.Sample Input:
2 32 10 3 10 5 2 6 6 8 1 9 8 76 7 9 1 10 7 3 4 8
Sample Output:
4 -1
Hint:
第一组数据选取{10,6,8,8} ,第二组数据无解。
题目链接:http://acm.zjut.edu.cn/ShowProblem.aspx?ShowID=1668
/* ----------------------------------------------------------------------------------
题目分析:题目的意思显然易见,就是求最少个数之和恰好等于m.按照贪心思想就是,先选择
一个比较m小,但是又是在那堆数里面最大的数,然后再如此选择下一个这样的数.直到将找到和
可以恰好等于又或者找不到为止.如果找到这些数,就直接返回.如果找不到这些数,就回溯到上
一步,重新选择一个比m,但是又是在那堆数里面未选择过的最大的数.如此反复,直到最后.
所以,最终做法就是利用回溯法.时间复杂度是O(nlgn)?是这个吗?我也不知道.遇到回溯题目的
时间复杂度总是不太会算.
PS:这里利用了类似于位图的方法,避免从大到小排序花费更多的时间.
---------------------------------------------------------------------------------- */
#include<stdio.h>
#include<memory.h>
int nNums[104] ; //记录相关数字的个数
int FindMinSelect(int m,int nLen,int nMaxNum) ;
int main(void)
{
int i = 0 ;
int n = 0 ;
int m = 0 ;
int t = 0 ;
int nMaxNum = 0 ;
int nTemp = 0 ;
int nLen = 0 ;
freopen("in.txt","r",stdin) ;
scanf("%d",&t) ;
while(t-- > 0)
{
memset(nNums,0,sizeof(nNums)) ;
scanf("%d%d",&m,&n) ;
nLen = 0 ;
nMaxNum = -1 ;
for(i = 0 ; i < n ; ++i)
{
scanf("%d",&nTemp) ;
nNums[nTemp]++ ;
if(nTemp > nMaxNum)
{
nMaxNum = nTemp ;
}
}
printf("%d\n",FindMinSelect(m,nLen,nMaxNum)) ; //nMaxNum为下一个开始搜索的数,也就是最大下标
}
return 0 ;
}
int FindMinSelect(int m,int nLen,int nMaxNum)
{
int i = 0 ;
int nResult = -1 ;
if(0 == m)
{
return nLen ;
}
else
{
for(i = nMaxNum ; i >= 1 ; --i)
{
if(nNums[i] != 0 && m >= i) //i既是索引又是要选择的数
{
m -= i ;
nNums[i]-- ;
nResult = FindMinSelect(m,nLen+1,i) ;
m += i ;
nNums[i]++ ;
if(nResult != -1)
{
return nResult ;
}
}
}
}
return nResult ;
}