题目大意:
有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
#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<<"="<
#define sp <<" "
#define ln <
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);
}