uvalive 7365 Composition 暴力解法

来源:互联网 发布:淘宝网新手开店教程 编辑:程序博客网 时间:2024/05/17 09:20

题意:输入n, m, k, 将n表示成多个数字相加的形式,但是但是不能包含对k取余余数为m的数字。

这个题正解是dp,但是数据量很小,只要姿势对,还是能爆过的,而且时间并不慢,9ms就跑完了,一开始用了记忆化搜索,但是去掉以后时间竟然是一样的。

这题其实一开始看了很久没看懂题意,看懂先随便暴了一下,样例出的就有点慢,然后换了种姿势重新暴,发现运行很快。

n最大30,如果枚举每个数字(也就是我一开始用的方法),会非常慢,有的数据10s都跑不完,但是我们可以换一种枚举方法,枚举每个数字出现了多少次,最后根据排列组合计算能排列成多少种答案,为了避免重复,我们保证先枚举到得数字总是比后枚举到得数字大。

    对于有重复数字的排列,用总体排列除以相同数字的内部排列(如果我描述的不清楚可以直接看代码或者问度娘),但是总体数字可能会超long long,所以对整体排列我直接除以了最小数字的内部排列,这样就可以避免超long long,因为除去最小数字以后数字个数就可以控制在15以内了。

#include <iostream>#include <cstring>#include <cstdio>#include <algorithm>using namespace std;typedef long long ll;int dp[40][40][40];int pw[100];int ans, m, k;int d[100];ll AAA(int u, int b) {    ll ans = 1;    for(int i = b + 1; i <= u; i++)        ans *= i;    return ans;}void dfs(int n, int mx, int u, int s) {    if(!n) {        ll t = AAA(s, d[u - 1]);        for(int i = 0; i < u - 1; i++)            t /= AAA(d[i], 0);        ans += t;        return;    }    for(int i = min(mx, n); i > 1; i--) {        if(i % k == m) continue;        for(int j = 1; j * i <= n; j++) {            d[u] = j;            dfs(n - i * j, i - 1, u + 1, s + j);        }    }    if(1 % k == m) return;    d[u] = n;    dfs(0, 0, u + 1, s + n);}void init() {    pw[0] = 1;    for(int i = 1; i <= 30; i ++)        pw[i] = pw[i - 1] * 2;    memset(dp, -1, sizeof(dp));}int main() {    int t, n, ks;    init();    scanf("%d", &t);    while(t--) {        scanf("%d%d%d%d", &ks, &n, &m, &k);        ans = 0;        dfs(n, n, 0, 0);//        dp[n][m][k] = ans;        printf("%d %d\n", ks, ans);    }    return 0;}



0 0
原创粉丝点击