[ZOJ 3682] E - Cup 3 (背包DP计数 + 滚动数组)

ZOJ - 3682

S1 个球迷支持巴萨, S2 个球迷支持皇马
球场有 N 个方阵,每个可以坐 ki 个球迷
安排球迷使得一个方阵要么全是支持巴萨的
要么全是支持皇马的,要么两种相等
问有几种安排方案

其实一看 N 最多只有200,但是 S1 S2$很大,我第一反应是离散化
然后就开了一个 map,结果依旧是爆空间了 (这题空间只有 65M)
所以只好老老实实地滚动数组了

因为确定了 S1 ,那么 S2 也可以确定,所以只要 S1 存一维就好
剩下的用类似背包DP的转移方式去转移就好了

#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <map>
#include <set>
#include <queue>
using namespace std;
typedef pair<int,int> Pii;
typedef long long LL;
typedef unsigned long long ULL;
typedef double DBL;
typedef long double LDBL;
#define MST(a,b) memset(a,b,sizeof(a))
#define CLR(a) MST(a,0)
#define Pow2(a) (a*a)

const int MOD=1000000007,maxs=1e5+10;
int S1,S2,N;
int inpt[210],psum[210];
LL dp[maxs];

int main()
{
    while(~scanf("%d%d%d", &S1, &S2, &N))
    {
        CLR(dp);
        dp[0]=1;
        for(int i=1; i<=N; i++)
        {
            scanf("%d", &inpt[i]);
            psum[i]=psum[i-1]+inpt[i];
        }
        for(int i=1; i<=N; i++)
        {
            for(int s1=S1; s1>=0; s1--)
            {
                int s2=S2-(psum[i-1]-s1);
                LL pre=dp[s1];
                dp[s1]=0;
                if(s1>=inpt[i]) dp[s1]+=dp[s1-inpt[i]];
                if(s2>=inpt[i]) dp[s1]+=pre;
                if(!(inpt[i]&1)&&s1>=inpt[i]/2&&s2>=inpt[i]/2) dp[s1]+=dp[s1-inpt[i]/2];
                dp[s1]%=MOD;
            }
        }
        printf("%lld\n", dp[S1]);
    }
    return 0;
}

你可能感兴趣的:(dp,ZOJ)