题目:http://oj.acm.zstu.edu.cn/JudgeOnline/problem.php?id=4432 ;
一开始想 先排序完再DFS, 写完递归毫无疑问超时了 由于时间原因跳过;
参考大神;
1.http://47.100.177.78/index.php/2018/12/09/zstu1001/
2.http://www.cnblogs.com/KafuuMegumi/p/10090646.html
第一种方法:
时间400+
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
int vis[40]={0,};
int v[40]={0,};
bool flag;
int n,m;
int sum[40]={0,};// 用来记录第i个硬币起之后所有硬币的和
void dfs(int now,int arr)//arr记录从第几个硬币开始搜索 FF now为累减至0,可少传入一个数据
{
if(now==0)
{
flag = true;
return;
}
if(now>sum[arr]) FF第一个剪枝:剩余需求大于剩下综合;
return;
for(int i=arr;i<=n;i++)
{
if(now>sum[i]) FF同第一个剪枝;
return;
if(vis[i-1]==0&&v[i-1]==v[i]) FF第二个剪枝:如果两个相同的 第一个不使用 第二个也不用考虑;
continue;
if(vis[i]==0&&now>=v[i])
{
vis[i]=1;
dfs(now-v[i],i+1);
if(flag)
return;
vis[i]=0;// FF复原
}
}
}
bool cmp(int a,int b)
{
return a>b;
}
int main(void)
{
int t,cas=0;
scanf("%d",&t);
while(t--)
{
flag = false;
memset(vis,0,sizeof(vis));
memset(sum,0,sizeof(sum));
cas++;
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&v[i]);
v[i+n]=v[i];
}
n*=2;
sort(v+1,v+n+1,cmp);
for(int i=n;i>=1;i--)
sum[i]=sum[i+1]+v[i];
dfs(m,1);
if(flag)
printf("Case %d: Yes\n",cas);
else
printf("Case %d: No\n",cas);
}
return 0;
}
第二种方法:
折半枚举 (巧妙)
先假设每种硬币只能选一次,枚举出所有可能性的和存到一个数组里(O(n*2^n)),然后把数组排序二分查找,比如一种可能性是a[i],那我们只要看k-a[i]是不是也在这个数组里就行了。
代码略;
if(binary_search(a,a+cnt,k-a[i]))flag=1;
附 meet in the middle算法
还未看
时间68;
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <set>
using namespace std;
int v[20]={0,};
bool flag;
int n,m,sum=0,na,nb;
int geta[20000]={-1,};
int getb[20000]={-1,};
void dfsa(int get,int l,int r)//arr记录从第几个硬币开始搜索
{
if(l>r)
{
getb[na++]=0;
return;
}
for(int i=0;i<=2;i++)
{
if(l==r)
geta[na++]=get+i*v[l];
else
dfsa(get+i*v[l],l+1,r);
}
return;
}
void dfsb(int get,int l,int r)//arr记录从第几个硬币开始搜索
{
if(l>r)
{
geta[nb++]=0;
return;
}
for(int i=0;i<=2;i++)
{
if(l==r)
getb[nb++]=get+i*v[l];
else
dfsb(get+i*v[l],l+1,r);
}
return;
}
int main(void)
{
int t,cas=0;
scanf("%d",&t);
while(t--)
{
flag = false;
na=nb=1;
cas++;
sum=0;
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&v[i]);
sum+=2*v[i];
}
if(sum<m)
{
printf("Case %d: No\n",cas);
continue;
}
dfsa(0,1,n/2);
dfsb(0,n/2+1,n);
sort(geta+1,geta+na);
sort(getb+1,getb+nb);
for(int i=1;i<na;i++)
{
if(geta[i]>m)
break;
if(geta[i]==geta[i-1])
continue;
int l=1,r=nb-1,mid;
while(l<=r)
{
mid=(l+r)/2;
if(getb[mid]<m-geta[i])
l=mid+1;
else if(getb[mid]>m-geta[i])
r=mid-1;
else
{
flag=true;
break;
}
}
if(flag)
break;
}
if(flag)
printf("Case %d: Yes\n",cas);
else
printf("Case %d: No\n",cas);
}
return 0;
}