很好的dp题目
我们尝试推断某一个人A能不间断的看完电影的条件
易得,A想要在看完B的第i-1部电影之后不间断的看B的第i部电影,应该满足以下条件:∑Atotk=1A[xk]+∑i−1k=1A[yk]−∑ik=1B[yk]≥0∑k=1AtotA[xk]+∑k=1i−1A[yk]−∑k=1iB[yk]≥0
那我们如果想求A,B均能顺利看完的方案数,考虑dp,大概要记录A的当前和,B的当前和,上面的式子对于A来说的历史最小值和对于B来说的历史最小值,每个状态的级别都是1000的,这样是O(10004)O(10004)的复杂度,不能接受
考虑怎么优化这个dp
我们可以发现一个性质:A和B中总有一个人能看完所有的电影,因为考虑A和B原来自己手上的电影,总有一个人会先看完自己手上原来的电影,另一个人后看完,那么那个后看完的人看完自己手上原来的电影以后,另一个人的电影一定已经全部看好放在他的队列里面了。也就是说,后看完自己手上原来的的电影的人一定能看完所有的电影
我们考虑用总方案数减去有人被卡住的方案数,由上面一段可知最多只会有一个人卡住,所以答案是∣总方案∣−∣A被卡住∣−∣B被卡住∣∣总方案∣−∣A被卡住∣−∣B被卡住∣
这样再用dp算,就可以缩减掉很多状态
令dp[i][j][k]表示当前考虑到第i部电影,对于1~i-1的所有位置,上面的多项式的值的最小值是j,当前的值是k的方案数。注意上面的多项式中的sigma(A)是动态变化的
转移有两种:
1. 第i部电影放入A的queue,那么之前的1~i的所有位置的值由于sigma(A)都要加上a[i],所以dp[i][j][k]→dp[i+1][j+a[i]][k+a[i]]dp[i][j][k]→dp[i+1][j+a[i]][k+a[i]]
2. 第i部电影放入B的queue,我们考虑用当前的k减去b[i]来更新历史最小值,并且k加上a[i]-b[i]
#include <cstdio>
#include <iostream>
#include <cstring>
#include <string>
#include <cstdlib>
#include <utility>
#include <cctype>
#include <algorithm>
#include <bitset>
#include <set>
#include <map>
#include <vector>
#include <queue>
#include <deque>
#include <stack>
#include <cmath>
#define LL long long
#define LB long double
#define x first
#define y second
#define Pair pair<int,int>
#define pb push_back
#define pf push_front
#define mp make_pair
#define LOWBIT(x) x & (-x)
using namespace std;
const int MOD=1e9+7;
const LL LINF=2e16;
const int INF=2e9;
const int magic=348;
const double eps=1e-10;
const double pi=acos(-1);
inline int getint()
{
char ch;int res;bool f;
while (!isdigit(ch=getchar()) && ch!='-') {}
if (ch=='-') f=false,res=0; else f=true,res=ch-'0';
while (isdigit(ch=getchar())) res=res*10+ch-'0';
return f?res:-res;
}
LL dp[2][2048][2048];
int a[148],b[148];
class TheMoviesLevelThreeDivOne
{
int n;
const int MMAX=1000;
inline LL calc()
{
int cur=0,nxt=1,i,j,k;
memset(dp[0],0,sizeof(dp[0]));
dp[0][MMAX][MMAX]=1;
for (i=1;i<=n;i++)
{
memset(dp[nxt],0,sizeof(dp[nxt]));
for (j=MMAX-20*i;j<=MMAX;j++)
for (k=MMAX-20*i;k<=MMAX+20*i;k++)
if (dp[cur][j][k])
{
//add i to y
dp[nxt][min(MMAX,j+b[i])][k+b[i]]+=dp[cur][j][k];
//add i to x
dp[nxt][min(j,k-a[i])][k+b[i]-a[i]]+=dp[cur][j][k];
}
swap(cur,nxt);
}
LL res=0;
for (j=MMAX-20*n;j<=MMAX-1;j++)
for (k=MMAX-20*n;k<=MMAX+20*n;k++)
res+=dp[cur][j][k];
return res;
}
public:
inline LL find(vector<int> A,vector<int> B)
{
n=int(A.size());int i;
for (i=1;i<=n;i++) a[i]=A[i-1],b[i]=B[i-1];
LL res=calc();
for (i=1;i<=n;i++) swap(a[i],b[i]);
res+=calc();
return (1ll<<n)-res;
}
};
/*---Debug Part---*/
int main ()
{
TheMoviesLevelThreeDivOne A;
int nn;vector<int> aa,bb;
while (scanf("%d",&nn)!=EOF)
{
register int i,x;
aa.clear();bb.clear();
for (i=1;i<=nn;i++) x=getint(),aa.pb(x);
for (i=1;i<=nn;i++) x=getint(),bb.pb(x);
printf("%lld\n",A.find(aa,bb));
}
return 0;
}