bzoj2287

背包+fft

既然要不选一个东西,那么我们求出前缀背包和后缀背包,每次答案就是f[i-1][w]*g[i+1][j-w]

但是这样复杂度还是n^3,跑不过,但是我们发现上面那个东西不就是个裸卷积吗,直接上fft,但是wa了...

wa的程序,大概是精度问题吧

#include<bits/stdc++.h>
using namespace std;
#define pi acos(-1)
const int N = 3010;
int n, m, L, x, nn, mm;
int r[N * 4], f[N][N], g[N][N], w[N];
complex<double> a[N * 4], b[N * 4];
void fft(complex<double> *a, int f)
{
    for(int i = 0; i < n; ++i) if(i < r[i]) swap(a[i], a[r[i]]);
    for(int i = 1; i < n; i <<= 1)
    {
        complex<double> t(cos(pi / i), f * sin(pi / i));
        for(int p = i << 1, j = 0; j < n; j += p)
        {
            complex<double> w(1, 0);
            for(int k = 0; k < i; ++k, w *= t)
            {
                complex<double> x = a[j + k], y = w * a[j + k + i];
                a[j + k] = x + y; a[j + k + i] = x - y;
            }
        } 
    } 
}
int main()
{
    scanf("%d%d", &nn, &mm);
    for(int i = 1; i <= nn; ++i) scanf("%d", &w[i]);
    f[0][0] = 1;
    for(int i = 1; i <= nn; ++i)
        for(int j = 0; j <= mm; ++j)
        {
            f[i][j] = f[i - 1][j];
            if(j >= w[i]) f[i][j] = (f[i][j] + f[i - 1][j - w[i]]) % 10;
        }
    g[nn + 1][0] = 1; 
    for(int i = nn; i; --i)
        for(int j = 0; j <= mm; ++j)
        {
            g[i][j] = g[i + 1][j];
            if(j >= w[i]) g[i][j] = (g[i][j] + g[i + 1][j - w[i]]) % 10;
        }
    for(int i = 1; i <= nn; ++i)
    {
        for(int j = 1; j <= mm; ++j) 
        {
            L = 0;
            m = 2 * j + 2;
            for(int k = 0; k <= m; ++k) a[k] = b[k] = 0;
            for(int k = 0; k <= j; ++k) 
            {
                a[k] = f[i - 1][k];
                b[k] = g[i + 1][k];
            }
            for(n = 1; n <= m; n <<= 1) ++L; 
            for(int k = 0; k < n; ++k) r[k] = (r[k >> 1] >> 1) | ((k & 1) << (L - 1));
            fft(a, 1);
            fft(b, 1);
            for(int k = 0; k <= n; ++k) a[k] = a[k] * b[k];
            fft(a, -1);
            int ans = (int)(a[j].real() / (double)n + 0.5);
            printf("%d", ans % 10);
        }
        puts("");
    }
    return 0;
}
View Code

 写了一个正解

f[i]:装满i的方案数

c[i][j]:装满j不用i的方案数

j<w[i],自然c[i][j]=f[j],因为w[i]装不下,不可能选

j>=w[i],c[i][j]=f[j]-c[i][j-w[i]],在j-w[i]填上一个w[i]就是j,表示选到第i个物品一定选了i的方案数,相减就是不选的方案数

#include<bits/stdc++.h>
using namespace std;
const int N = 2010;
int n, m;
int w[N], c[N], f[N];
int main()
{
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; ++i) scanf("%d", &w[i]);
    f[0] = 1;
    for(int i = 1; i <= n; ++i)
        for(int j = m; j >= w[i]; --j)
            f[j] = (f[j] + f[j - w[i]]) % 10;
    for(int i = 1; i <= n; ++i)
    {
        for(int j = 0; j <= m; ++j)
        {
            if(j >= w[i]) c[j] = (f[j] - c[j - w[i]] % 10 + 10) % 10;
            else c[j] = f[j];
            if(j > 0) printf("%d", c[j]);
        }
        puts("");
    }
    return 0;
}
View Code

 

posted @ 2017-08-19 13:51  19992147  阅读(242)  评论(0编辑  收藏  举报