UOJ【NOIP2014】飞扬的小鸟 背包dp
codevs 3729 飞扬的小鸟
苟且抄DQS代码。
题解都在代码注释中……
#include <cstdio>
#include <cstring>
#include <iostream>
#include <cstdlib>
using namespace std;
#define MAXN (10010)
#define MAXM (1010)
int dp[MAXN][MAXM];
struct CLICK{
int up, down;// X 和 Y
}w[MAXN];
struct GUANZI{ // 管子
int l, r;
}l[MAXN];
int main()
{
int n, m, k;
cin >> n >> m >> k;// 长度, 高度, 管子数量
for(int i = 0; i <= n-1; ++ i) scanf("%d%d", &w[i].up, &w[i].down);// X 和 Y
for(int i = 1; i <= n; ++ i) l[i].l = 0, l[i].r = m+1; // 初始化上限和下限
for(int i = 1; i <= k; ++ i)
{
int p;
scanf("%d", &p);// 管子位置
scanf("%d%d", &l[p].l, &l[p].r);//更新限制
}
#define INF (1e9)
//初始化dp数组,从 dp[0][j](j != 0)开始飞
dp[0][0] = INF;
for(int i = 1; i <= n; ++ i)
for(int j = 0; j <= m; ++ j)
dp[i][j] = INF;
for(int i = 1; i <= n; ++ i)
{
for(int j = 1; j <= m; ++ j)
{
if(j >= w[i-1].up)// 如果 j 可以由q前一步蹦一下转移而来
{
dp[i][j] = min(dp[i][j], dp[i-1][j-w[i-1].up]+1);// 蹦一下
dp[i][j] = min(dp[i][j], dp[i][j-w[i-1].up]+1);//为 完全背包 提供可能 蹦好多下
}
if(j == m)//如果到了最上面
{
for(int k = j-w[i-1].up; k <= m; ++ k)// 枚举可以跳到 m 的高度 k
{
dp[i][j] = min(dp[i][j], dp[i-1][k]+1);// 蹦一下
dp[i][j] = min(dp[i][j], dp[i][k]+1);//为 完全背包 提供可能 蹦好多下
}
}
}
for(int j = 1; j <= m; ++ j)// 高度
if(j + w[i-1].down <= m)// 可以掉落至 j
dp[i][j] = min(dp[i][j], dp[i-1][j+w[i-1].down]);//掉落至 j 只能掉一下,不花费点击次数
// 管子们的不合法位置
for(int j = 0; j <= l[i].l; ++ j) dp[i][j] = INF;
for(int j = l[i].r; j <= m; ++ j) dp[i][j] = INF;
}
int ans1 = INF; //最少点击屏幕数
for(int j = 1; j <= m; ++ j)
ans1 = min(ans1, dp[n][j]);//到达 dp[n][j]
if(ans1 < INF)
{
printf("1\n%d\n", ans1);
return 0;
}
// 无法到达 dp[n][j]
int maxlong = 1; // 最远的 i
// 找最远的 i
for(int i = 1; i <= n; ++ i)
{
bool h = 0;
for(int j = 1; j <= m; ++ j)
{
if(dp[i][j] < INF)
{
h = 1;
break;
}
}
if(!h)
{
maxlong = i;
break;
}
}
int ans2 = 0; //最多通过多少个管道
for(int i = 1; i < maxlong; ++ i)
if(l[i].l || l[i].r != m+1) // l, r任意一个被改变过即说明有管子
++ ans2;
printf("0\n%d\n", ans2);
return 0;
}