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


    是当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=0;i<c;i++){
            if(c==now)continue;
            value[i].clear();
            for(int j=0;j<k[i];j++)
                value[i].insert(y[i][j]);//放到集合里去重 
        }
        
        for(int t=0;;t++){
            for(int i=0;i<k[now];i++){
                ll n=(ll)x[now]*t+y[now][i];
                if(n==0)continue;
                
                //验证n是否是可行解 
                bool ok=1;
                for(int i=0;i<c;i++){
                    if(i==now)continue;
                    if(!value[i].count(n%x[i])){
                        ok=0;
                        break;
                    }
                }
                
                if(ok){
                    cout<<n<<endl;
                    if(--s==0)return;
                }
            } 
        }
    }
    
    
    //以下是暴力枚举每个组合方案的解法 
    long long exgcd(long long a, long long b, long long &x, long long &y) {//比较好的exgcd() 
        if (!b) {x = 1; y = 0; return a;}
        long long d = exgcd(b, a % b, y, x);
        y -= a / b * x;
         return d;
    }
    ll china(){//mx+ny=1
        ll M=1,ans=0;
        for(int i=0;i<c;i++)M*=x[i];
        for(int i=0;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=0;i<k[d];i++){
            a[d]=y[d][i];
            dfs(d+1);
        }
    }
    void solve_china(){
        ans.clear();
        dfs(0);//一轮搜索出组合内的所有可行解 
        sort(ans.begin(),ans.end());//排序答案 
        ll M=1;
        for(int i=0;i<c;i++)M*=x[i];
        
        for(int i=0;;i++)
            for(int j=0;j<ans.size();j++){
                ll n=M*i+ans[j];
                if(n>0){
                    cout<<n<<endl;
                    if(--s==0)
                        return;
                }
            }
    }
    
    int main(){
        while(cin>>c>>s && c && s){
            now=0;
            ll sum=1;
            for(int i=0;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=0;j<k[i];j++)
                    cin>>y[i][j];
                sort(y[i],y[i]+k[i]);//从小到大排序 
            }
            if(sum>10000)
                solve_enum();//当y组合较多时直接枚举N 
            else solve_china();//当y组合不是很多时枚举所有可能 
            puts("");
        }
    }
  • 相关阅读:
    linux socat创建简单的tun隧道
    【k8s】sc-nfs-pod
    c#中equals和==
    数据结构之哈希表
    数据结构之红黑树
    数据结构之2-3查找树
    数据结构之二叉查找树
    数据结构之递归与栈
    数据结构之二分查找法(折半查找)
    数据结构之基于无序链表的集合和映射
  • 原文地址:https://www.cnblogs.com/zsben991126/p/10438794.html
Copyright © 2020-2023  润新知