题目大意:
有n个房间,i和i+1之间有扇门,能被打开当且仅当第i个房间有ai个人按下按钮或者第i+1个房间有bi个人按下按钮。门打开后人可以双向通过。按按钮的人不能移动,一旦放开按钮门就会关死。现在要求你在每个房间放一些人,使得放的总人数最多,并且不存在一种方案使得第1个房间有至少m个人。n<=1000,m,ai,bi<=10000;
题解:神仙dp。
考虑这个操作是可逆的。考虑最后一定是让所有人尽量往左匀,但是最后1房间只有少于m个人。那么反过来就是枚举每个房间有多少人并尽量向右匀,但是要保证任意时刻右面的房间不能匀到左边来。那么设dp[i,j]表示第i个房间能从左边匀出j个人待在i房间时,答案是多少。
那么若
j
<
a
i
j<a_i
j<ai,那么i+1房间要么有少于bi个人:
d
p
(
i
,
j
)
+
k
→
d
p
(
i
+
1
,
k
)
,
k
<
b
i
dp(i,j)+k\rightarrow dp(i+1,k),k<b_i
dp(i,j)+k→dp(i+1,k),k<bi,要么i+1房间恰好有bi个人,这时可以把这j个人匀到右边去:
d
p
(
i
,
j
)
+
j
→
d
p
(
i
+
1
,
j
+
b
i
)
dp(i,j)+j\rightarrow dp(i+1,j+b_i)
dp(i,j)+j→dp(i+1,j+bi);
否则若
a
i
≤
j
<
a
i
+
b
i
a_i\le j<a_i+b_i
ai≤j<ai+bi,那么可以将i房间的j-ai个人匀到i+1房间(并且i+1房间不能有人,否则可以从右边匀到左边):
d
p
(
i
,
j
)
→
d
p
(
i
+
1
,
j
−
a
i
)
dp(i,j)\rightarrow dp(i+1,j-a_i)
dp(i,j)→dp(i+1,j−ai);
否则
a
i
+
b
i
≤
j
a_i+b_i\le j
ai+bi≤j,那么所有人都可以到右边:
d
p
(
i
,
j
)
→
(
i
+
1
,
j
)
dp(i,j)\rightarrow(i+1,j)
dp(i,j)→(i+1,j)
可以发现
j
≤
max
(
m
,
max
(
a
i
+
b
i
)
)
j\le \max\left(m,\max(a_i+b_i)\right)
j≤max(m,max(ai+bi))。
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define Rep(i,v) rep(i,0,(int)v.size()-1)
#define lint long long
#define ull unsigned lint
#define db long double
#define pb push_back
#define mp make_pair
#define fir first
#define sec second
#define gc getchar()
#define debug(x) cerr<<#x<<"="<<x
#define sp <<" "
#define ln <<endl
using namespace std;
typedef pair<int,int> pii;
typedef set<int>::iterator sit;
inline int inn()
{
int x,ch;while((ch=gc)<'0'||ch>'9');
x=ch^'0';while((ch=gc)>='0'&&ch<='9')
x=(x<<1)+(x<<3)+(ch^'0');return x;
}
const int inf=-1e7,N=1003,M=20004;int a[N],b[N],f[2][M];
inline void upd(int &x,int y) { x=max(x,y); }
int main()
{
int n=inn(),m=inn(),k=m;
rep(i,1,n-1) a[i]=inn(),b[i]=inn(),upd(k,a[i]+b[i]);
rep(i,0,m-1) f[1][i]=i;rep(i,m,k) f[1][i]=inf;
rep(i,1,n)
{
int pf=inf,*now=f[i&1],*nxt=f[(i+1)&1];
rep(j,0,k) nxt[j]=inf;
rep(j,0,k) if(now[j]>inf)
{
if(j<a[i]) upd(nxt[j+b[i]],now[j]+b[i]),upd(pf,now[j]);
else if(j<a[i]+b[i]) upd(nxt[j-a[i]],now[j]);
else upd(nxt[j],now[j]);
}
rep(j,0,b[i]-1) upd(nxt[j],pf+j);
}
int ans=inf,*now=f[n&1];rep(i,0,k) upd(ans,now[i]);
return !printf("%d\n",ans);
}