uva11754 中国剩余定理+暴力搜索

时间:2023-12-21 08:21:32

是当y的组合数较小时,暴力枚举所有组合,然后用中国剩余定理求每种组合的解,对解进行排序即可

注意初始解可能是负数,所以如果凑不够S个,就对所有解加上M,2M。。。。

当y的组合数较大时,选择一个k/x最小的序列,枚举N=x*t+y,外层枚举t,内层枚举y,然后验证N是否是可行解

为什么要选k/x最小的:就是相对于x,k越小越好,使更多机会枚举t,

#include<bits/stdc++.h>
#include<vector>
#include<set>
using namespace std;
#define maxn 105
#define ll long long int c,s,x[maxn],k[maxn],y[maxn][maxn],now;
set<int> value[maxn];
vector<ll> ans;
ll a[maxn]; //以下是枚举N的解法,找到 k/x 最小的条件,按照t=0,1,2..的顺序枚举 N = X*t+Y
void solve_enum(){
for(int i=;i<c;i++){
if(c==now)continue;
value[i].clear();
for(int j=;j<k[i];j++)
value[i].insert(y[i][j]);//放到集合里去重
} for(int t=;;t++){
for(int i=;i<k[now];i++){
ll n=(ll)x[now]*t+y[now][i];
if(n==)continue; //验证n是否是可行解
bool ok=;
for(int i=;i<c;i++){
if(i==now)continue;
if(!value[i].count(n%x[i])){
ok=;
break;
}
} if(ok){
cout<<n<<endl;
if(--s==)return;
}
}
}
} //以下是暴力枚举每个组合方案的解法
long long exgcd(long long a, long long b, long long &x, long long &y) {//比较好的exgcd()
if (!b) {x = ; y = ; return a;}
long long d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
ll china(){//mx+ny=1
ll M=,ans=;
for(int i=;i<c;i++)M*=x[i];
for(int i=;i<c;i++){
ll w=M/x[i],xx,yy;
exgcd(x[i],w,xx,yy);//拓展欧几里得解出
ans=(ans+w*yy*a[i])%M;
}
return (ans+M)%M;
} void dfs(int d){
if(d==c){
ans.push_back(china());//用中国剩余定理求出当前组合的结果
return;
}
for(int i=;i<k[d];i++){
a[d]=y[d][i];
dfs(d+);
}
}
void solve_china(){
ans.clear();
dfs();//一轮搜索出组合内的所有可行解
sort(ans.begin(),ans.end());//排序答案
ll M=;
for(int i=;i<c;i++)M*=x[i]; for(int i=;;i++)
for(int j=;j<ans.size();j++){
ll n=M*i+ans[j];
if(n>){
cout<<n<<endl;
if(--s==)
return;
}
}
} int main(){
while(cin>>c>>s && c && s){
now=;
ll sum=;
for(int i=;i<c;i++){
cin>>x[i]>>k[i];
sum*=k[i];
if(k[i]*x[now]<k[now]*x[i])
now=i;//找到k/x最小的条件,按照t=0,1,2..的顺序枚举 N = X*t+Y
for(int j=;j<k[i];j++)
cin>>y[i][j];
sort(y[i],y[i]+k[i]);//从小到大排序
}
if(sum>)
solve_enum();//当y组合较多时直接枚举N
else solve_china();//当y组合不是很多时枚举所有可能
puts("");
}
}