UVA 11754 Code Feat (中国剩余定理+枚举)

题意:对于C个条件,每个条件都有X和k个y,找出N,使得N对于每个条件,都有N%X == 某一个y,然后以递增次序输出S个满足的N。

Input consists of several test cases. Each test case starts with a line containing C, the number of clues(1 ≤ C ≤ 9), and S, the number of desired solutions (1 ≤ S ≤ 10). The next C lines each startwith two integers X (2 ≤ X) and k (1 ≤ k ≤ 100), followed by the k distinct integers Y1, Y2, . . . , Yk(0 ≤ Y1, Y2, . . . , Yk < X).
You may assume that the X’s in each test case are pairwise relatively prime (ie, they have nocommon factor except 1). Also, the product of the X’s will fit into a 32-bit integer.
The last test case is followed by a line containing two zeros.

题解:中国剩余定理+枚举
条件 N%X == yi 可以表示为 N ≡ yi (mod X),那么就可以用中国剩余定理求解最小N,但是会t,考虑两种情况。

用tot表示所有k的乘积,即余数的组合数。

①当tot较小时,可以dfs枚举y的每一种组合,然后跑crt,求出每种组合最小的N,递增输出S个即可。但可能不够,考虑C个X两两互质,那它们的乘积就是lcm,外层循环累加即可。

②当tot较大时,我们从C个条件中选择出最优的一个条件,根据这个最优条件,枚举商i生成N,N = i × X[best] + y[best][j],然后取模其他除数X得到r,看对应余数集合中能否有取模后到余数r。如果最优的X小的话,那么i必然要枚举更多次,故而X要尽量大,如果余数的数量k太多的话,枚举也会增多,故而k要尽量小,综合即是X/k要最大。

如果仅使用枚举的话,还是会t。注意y可能为0。

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#include<queue>
#include<stack>
#include<cmath>
#include<vector>
#include<fstream>
#include<set>
#include<map>
#include<sstream>
#include<iomanip>
#define ll long long
using namespace std;
int c, s, x[11], k[11], y[11][111], bestc;
ll tot;
int a[11];  //存储当前组合
vector<int> sol;  //存储每个组合的可行N
set<int> vis[15]; //存储除bestc外的余数yi
void gcd(ll a, ll b, ll& d, ll& x, ll& y) {
    if (!b) {
        d = a;
        x = 1;
        y = 0;
    }
    else {
        gcd(b, a % b, d, y, x);
        y -= x * (a / b);
    }
}
int china(int n, int* s, int* m) {
    ll M = 1, d, y, x = 0;
    for (int i = 1; i <= n; i++) M *= m[i];
    for (int i = 1; i <= n; i++) {
        ll w = M / m[i];
        gcd(m[i], w, d, d, y);
        x = (x + y * w * a[i]) % M;
    }
    return (x + M) % M;
}
void dfs(int dep) {  //枚举y的所有可能组合
    if (dep == c + 1) sol.push_back(china(c, a, x));
    else {
        for (int i = 1; i <= k[dep]; i++) {
            a[dep] = y[dep][i];
            dfs(dep + 1);
        }
    }
}
void solveChina() {
    sol.clear();
    dfs(1);
    sort(sol.begin(), sol.end());
    ll M = 1;
    for (int i = 1; i <= c; i++) M *= x[i];  //M为lcm
    for (int i = 0; s; i++) {
        for (int j = 0; j < sol.size(); j++) {
            ll n = M * i + sol[j];
            if (n > 0) {
                printf("%lld\n", n);
                if (--s == 0) break;
            }
        }
    }
}
void solve(int bestc) {
    for (int i = 1; i <= c; i++) {
        if (i == bestc) continue;
        vis[i].clear();  //set数组只能挨个清空
        for (int j = 1; j <= k[i]; j++) vis[i].insert(y[i][j]);
    }
    for (int i = 0; s; i++) {  //从小到大枚举
        for (int j = 1; j <= k[bestc]; j++) {
            ll n = 1ll * x[bestc] * i + y[bestc][j];
            if (n == 0) continue;
            int flag = 1;
            for (int k = 1; k <= c; k++) {  //判断其他组的条件是否满足
                if (k == bestc) continue;
                if (vis[k].find(n % x[k]) == vis[k].end()) {
                    flag = 0;
                    break;
                }
            }
            if (flag) {
                printf("%lld\n", n);
                if (--s == 0) break;
            }
        }
    }
}
int main() {
	while (~scanf("%d%d", &c, &s) && c) {
		bestc = tot = 1;
		for (int i = 1; i <= c; i++) {
			scanf("%d%d", &x[i], &k[i]);
			tot *= k[i];
			for (int j = 1; j <= k[i]; j++) {
				scanf("%d", &y[i][j]);
			}
			sort(y[i] + 1, y[i] + k[i] + 1);   //便于枚举和二分查找
			if (k[i] * x[bestc] < k[bestc] * x[i]) bestc = i;
		}
        if (tot > 1e4) solve(bestc);
		else solveChina();
        printf("\n");
	}
	return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值