TG 寒假-模拟 第二场 题解

本场比赛较简单,恭喜xcy58神仙和chengxx神仙AK(热烈祝贺)。

T1 水

这题应该不难看出求n个瓶子最少可以合并为几个瓶子就是求二进制下的n有多少个1。

然后就完了……每次增加一些瓶子,让新瓶子数在二进制一的数量下比原来的少就好。

#include<iostream>
using namespace std;
unsigned long long n,a,b,shu=0,k=0;
int main()
{
	cin>>a>>b;
	while(1)
	{
		n=a;
		shu=0;
		while(n!=0)
		{
			if(n%2==1)
			{
				shu++;
			}
			n/=2;
		}
		if(shu<=b)
		{
			cout<<k<<endl;
			return 0;
		}
		k+=(a&-a);
		a+=(a&-a);
	}
	return 0;
}

(没用scanf,大佬轻喷)

T2 朋友

简单并查集,模板题……

话说这题洛谷数据超级水,我故意写了好几个错的,洛谷全是满分(啊这)

直接贴代码了

#include<iostream>
using namespace std;
int z,n,m,a1,a2,a3,sz[1000005]; 
int h(int zz)
{
    if(sz[zz]==zz)return zz;
    return sz[zz]=h(sz[zz]);
}
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        sz[i]=i;
    }
    for(int i=0;i<m;i++)
    {
        cin>>a1>>a2>>a3;
        if(a1==1)
        {
            sz[h(a2)]=h(a3);
        }
        else if(a1==2)
        {
            if(h(a2)==h(a3))
            {
                cout<<"Y"<<endl;
            }
            else
            {
                cout<<"N"<<endl;
            }
        }
    }
    return 0;
}

T3 托盘天平

啊这题我原来的标程写错了,导致数据错了一个点,重新翻看后发现深搜写的有点弱智……现在改对了,还能当题解……

这题觉得需要简单的讲一下,他需要用到dp和dfs,dfs有两种搜法,搜剩下的和搜去掉的,看看数据范围就能发现搜去掉的显然比搜剩下的不知道快多少……然后就爆搜求出哪些砝码被去掉了,然后是dp,dp的话用sz数组表示这个数有没有出现过,转移方程也不难,那个bl是用来加速的,从之前出现过最大的开始向下搜比从所有可能里最大的往下搜要快。然后……就完了。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
long long a[30],bj[30],c[30],sz[4000];
long long int n,m;
long long jg=0;
long long ans=0;
long long zlzs=0;
long long bl=0;
void dp()
{
    memset(sz,0,sizeof(sz));
    sz[0]=1;
    zlzs=0;
    bl=0;
    for(int i=1;i<=n;i++)
    {
        if(bj[i]==0) 
        {
            for(int j=bl;j>=0;j--)
            {
                if(sz[j]==1&&sz[j+a[i]]==0)
                {
                    sz[j+a[i]]=1;
                    zlzs++; 
                }
            }
            bl+=a[i];
        }
    }
    jg=max(jg,zlzs);
}
void dfs(long long wz,long long shu)
{
    if(shu>m)return;
    if(wz==n+1)
    {
        if(shu==m)dp();
        return; 
    }
    dfs(wz+1,shu);
    bj[wz]=1;
    dfs(wz+1,shu+1);
    bj[wz]=0;
    return;
}
int main()
{
    scanf("%lld%lld",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&a[i]);
    }
    dfs(1,0);
    printf("%lld
",jg);
    return 0;
} 

T4 购物

啊这个题看看数据范围,n<=16我觉得会有一种感觉,这题不是深搜就是状压,这题是状压。

前缀和记录一下前i个商品的价格之和,便于查找,然后转移,转移的过程中求某个硬币可以买多少个商品是要用二分,要不然(在原题中)会超时。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
long long a[30],dp[100005],sz[100005],qzh[100005],zd=-99999999;
long long k,n,ans;
long long jg=0;
void cl(int shu)
{
	ans=0;
	for(int i=0;i<=k-1;i++)
	{
		if((shu>>i)%2==0)
		{
			ans+=a[i];
		}
	}
	zd=max(zd,ans);
}
long long hs(long long zq,long long wz)
{
	long long xwz=-1,l=wz+1,r=n-1;
	while(l<=r)
	{
		long long mid=(l+r)/2;
		if(qzh[mid]-qzh[wz]<=a[zq])
		{
			xwz=mid;
			l=mid+1;
		}else
		{
			r=mid-1;
		}
	}
	return xwz;
}
int main()
{
	scanf("%lld%lld",&k,&n);
	for(int i=0;i<k;i++)
	{
		scanf("%lld",&a[i]);
	}
	for(int i=0;i<n;i++)
	{
		scanf("%lld",&sz[i]);
		qzh[i]=qzh[i-1]+sz[i];
	}
	memset(dp,-1,sizeof(dp));
	sort(a,a+k);
	for(int i=0;i<(1<<k);i++)
	{
		for(int j=0;j<k;j++)
		{
			if((i>>j)%2==1)
			{
				dp[i]=max(dp[i],hs(j,dp[i-(1<<j)]));
			}
		}
	}
	for(int i=0;i<(1<<k);i++)
	{
		if(dp[i]==n-1)
		{
			cl(i);
		}
	}
	if(zd!=-99999999)
	{
		cout<<zd<<endl;
	}else
	{
		cout<<-1<<endl;
	}
	return 0;
}

为我第三题zz的标程道歉

原文地址:https://www.cnblogs.com/lichangjian/p/14336636.html