【noi.ac #2009】escape

题目

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。

思路

首先,所有的操作是可逆的
所以我们可以考虑合并一些状态。我们可以不停地合并能走在一起的人,那这样就会出现几种情况:

  1. 有若干个大段
  2. 每个大段里有若干小段,小段里面的人走不出去(按开关),剩下的人可以在大段内自由移动

我们发现能到达某个房间的人数是固定的
所以我们设f[i][j]为第i个房间有j个人,前i个房间的最大人数。转移如下:

  1. j<a[i]时,转移到 f[i+1][j+b[i]]或 f[i+1][0~b[i]-1]
  2. a[i]<=j<a[i]+b[i]时,转移到 f[i+1][j-a[i]]
  3. 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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值