UVA11754 - Code Feat

链接

https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=2854

题解

这个做法有些玄学
X = ∏ x i X=\prod x_i X=xi
我找 k i X x i \frac{k_iX}{x_i} xikiX最小的,然后枚举 a n s = k x i + y j ans=kx_i+y_j ans=kxi+yj,(注意这里的 k k k和之前意义不同)
再依次检验这个解是不是也 x x x满足其它 c − 1 c-1 c1个条件,在 ( 0 , X ] (0,X] (0,X]内寻找解,如果解不够 S S S个,就加上 k × X k\times X k×X来构造剩余的解
复杂度是 O ( C X k i x i ) O(C\frac{Xk_i}{x_i}) O(CxiXki)

算法复杂度分析

这个复杂度非常的玄学,
C = 1 C=1 C=1时,这个算法显然是 O ( S ) O(S) O(S)
C = 2 C=2 C=2时,极端数据就是 x 1 , x 2 x_1,x_2 x1,x2都接近 2 16 2^{16} 216 k 1 = k 2 = 100 k_1=k_2=100 k1=k2=100,这个复杂度也不高
C = 3 C=3 C=3时,极端数据就是 x 1 , x 2 , x 3 x_1,x_2,x_3 x1,x2,x3都接近 2 32 3 2^{\frac{32}{3}} 2332 k 1 = k 2 = k 3 = 100 k_1=k_2=k_3=100 k1=k2=k3=100,代入上式会发现时间复杂度表达式的值高达 1 0 8 10^8 108数量级,但是为啥算法还是跑的很快?因为 k k k太大了,解分布的很密集,因此我还是很快就找到了前 S S S个解
所以我的实际计算次数其实和解分布的密集程度成负相关,那如果我让 k k k很小,是个什么情况?
比如这样的极端数据: C = 9 C=9 C=9 x i x_i xi是前 9 9 9个素数 2 , 3 , 5 , 7 , 11 , 13 , 17 , 19 , 23 2,3,5,7,11,13,17,19,23 2,3,5,7,11,13,17,19,23 k i = 1 k_i=1 ki=1
那显然最小的解不会超过这9个素数的乘积 223092870 223092870 223092870,按照我的算法,我会枚举答案 N = y 1 , 23 + y 1 , 46 + y 1 . . . . N=y_1,23+y_1,46+y_1.... N=y1,23+y1,46+y1....,然后带入剩下的条件验证,实际计算次数不会超过 9699690 9699690 9699690
上述情形已经是极端情形,如果我让任何一个 k i k_i ki增加,那么解的分布就会更密集,我就会更快找到答案,而答案变密集造成的影响远比 k i k_i ki的值增加对复杂度的影响更大

思路提取

主要思路就是,如果一道题让你找满足 C C C个条件的某种解,那么可以先看一种条件,在这种条件下枚举解,在去验证其是否符合其它条件

代码

//数论
#include <bits/stdc++.h>
#define maxn 110
#define ll long long
#define linf (1ll<<60)
using namespace std;
set<ll> y[maxn];
ll C, S, x[maxn], X, lis[maxn];
ll read(ll x=0)
{
	ll c, f=1;
	for(c=getchar();!isdigit(c);c=getchar())if(c=='-')f=-1;
	for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+c-48;
	return f*x;
}
void init()
{
	ll i, j, k;
	*lis=0;
	X=1;
	for(i=1;i<=C;i++)
	{
		x[i]=read();
		X*=x[i];
		k=read();
		y[i].clear();
		for(j=1;j<=k;j++)y[i].insert(read());
	}
}
bool check(ll N)
{
	ll i;
	for(i=1;i<=C;i++)if(y[i].find(N%x[i])==y[i].end())return false;
	return true;
}
void work()
{
	ll i, j, k, b, N, t=linf, pos, res=S+1;
	set<ll>::iterator it;
	for(i=1;i<=C;i++)if(X/x[i]*y[i].size()<t)t=X/x[i]*y[i].size(), pos=i;
	for(k=0;k*x[pos]+x[pos]-1<=X and res;k++)
	{
		for(it=y[pos].begin();it!=y[pos].end() and res;it++)
		{
			N=k*x[pos]+*it;
			if(check(N))
			{
				res--;
				lis[++*lis]=N;
			}
		}
	}
	if(lis[1]==0)S++;
	t=*lis;
	while(*lis<=S)++*lis, lis[*lis]=lis[*lis-t]+X;
	for(i=1;i<=S;i++)if(lis[i])printf("%lld\n",lis[i]);
	putchar(10); 
}
int main()
{
	while(scanf("%lld%lld",&C,&S),C)init(), work();
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值