1.baby coins

题目: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;
}

猜你喜欢

转载自blog.csdn.net/weixin_43879012/article/details/84953995