题目
statement
有nn个房间,编号为1到nn。
有n−1n−1个隧道,第ii个隧道连接房间ii和i+1i+1。隧道在正常情况下是关闭着的,要打开第ii个隧道需要有aiai个人在房间ii按住开关或者bibi个人在房间i+1i+1按住开关。按开关的人不能进行任何其它操作(比如移动或者同时按另一个开关),一旦他们松开开关,隧道会立刻关上。
在房间1有一个隧道通往出口,要打开这个隧道需要mm个人按住开关。你想知道在保证这个隧道在任何时刻都不会被打开的情况下,最多可以在这些房间中安排多少个人(你可以指定他们初始在哪个房间)。
input description
第一行两个整数n,mn,m。
接下来n−1n−1行每行两个整数ai,biai,bi。
output description
一行一个整数表示答案。
2 20
5 5
19
2 20
5 20
24
7 7
2 8
6 6
1 1
2 4
2 8
7 8
23
点此下载大样例
Notes
Subtask 1 (5pts):ai=bi=1ai=bi=1。
Subtask 2 (23pts):m,ai,bi≤2m,ai,bi≤2。
Subtask 3 (39pts):n,m,ai,bi≤200n,m,ai,bi≤200。
Subtask 4 (33pts):无特殊限制。
对于全部数据,1≤n≤10001≤n≤1000,1≤m,ai,bi≤100001≤m,ai,bi≤10000。
思路
首先,所有的操作是可逆的
所以我们可以考虑合并一些状态。我们可以不停地合并能走在一起的人,那这样就会出现几种情况:
- 有若干个大段
- 每个大段里有若干小段,小段里面的人走不出去(按开关),剩下的人可以在大段内自由移动
我们发现能到达某个房间的人数是固定的
所以我们设f[i][j]为第i个房间有j个人,前i个房间的最大人数。转移如下:
- j<a[i]时,转移到 f[i+1][j+b[i]]或 f[i+1][0~b[i]-1]
- a[i]<=j<a[i]+b[i]时,转移到 f[i+1][j-a[i]]
- a[i]+b[i]<=j 时,转移到 f[i+1][j]。
代码
#include<bits/stdc++.h>
using namespace std;
const int inf=0x3f3f3f3f,MXP=20000,N=1077;
int f[N][MXP+77],ans=0,n,m,a[N],b[N];
/*void dfs(int u,int s)
{
vis[u]=inq[u]=1;dis[u]=s;
for(it i=e[u].begin(); i!=e[u].end(); i++)
{
int v=i->v;
if(inq[v])
{
if(s-dis[v]+i->w)
printf("%d\n",s-dis[v]+i->w);
else printf("%d %d\n",s-dis[v],i->w);
exit(0);
}
if(vis[v]) continue;
dfs(v,s+i->w);
}
inq[u]=0;
}*/
int main()
{
scanf("%d%d",&n,&m);
for(int i=1; i<n; i++) scanf("%d%d",&a[i],&b[i]);
for(int i=0; i<=n; i++) for(int j=0; j<=MXP; j++) f[i][j]=-inf;
for(int i=0; i<m; i++) f[1][i]=i;
for(int i=1; i<n; i++)
{
int yjy=-inf;
for(int j=0; j<=MXP; j++)
{
if(f[i][j]<0) continue;
if(j<a[i])
{
yjy=max(yjy,f[i][j]);
f[i+1][j+b[i]]=max(f[i+1][j+b[i]],f[i][j]+b[i]);
}
if(j>=a[i]&&j<a[i]+b[i]) f[i+1][j-a[i]]=max(f[i+1][j-a[i]],f[i][j]);
if(j>=b[i]+a[i]) f[i+1][j]=max(f[i+1][j],f[i][j]);
}
for(int j=0; j<b[i]; j++) f[i+1][j]=max(f[i+1][j],yjy+j);
}
for(int i=0; i<=MXP; i++) ans=max(ans,f[n][i]);
printf("%d",ans);
}