noip2014飞扬的小鸟
飞扬的小鸟
题意:
一个二维平面,长为\(n\),宽为\(m\),其中有\(k\)个管道,小鸟从最左边任意整数高度位置出发,到达游戏界面最右边时,游戏完成。
小鸟每个单位时间沿着\(x\)轴向右移动一个单位距离,小鸟在从\(i\)移动到\(i +1\)时,玩家可以选择是否点击屏幕(可以点击多次),若不点击,小鸟将下降\(y_i\)单位距离,若点击\(j\)次,小鸟的上升距离为\(j\times x_i\)。
当小鸟碰到管壁或纵坐标等于\(0\)时,游戏结束。小鸟的高度可以不停上升,但最高不超过\(m\),(可以到达\(m\),游戏不会结束)。
管道:并不是每个横坐标都对应一个管道,管道有一个上边沿高度和一个下边沿高度。
这样一个图片便于理解题意:
蓝色直线表示小鸟的飞行轨迹,红色直线表示管道。
求是否可以完成游戏,如果可以,求最小点击次数,如果不可以,求最多可以通过多少个管道缝隙。
数据范围:
\(5\le n\le10000,5\le m\le1000\)
\(70\,\,points\)
这个\(dp\)方程除了跳到顶比较难处理以外,还是很容易想到的,
但相对的,时间复杂度也很大,\(O(nm^2)\)
for(int i = 1; i <= n; i++)
{
for(int j = max(L[i], x[i - 1]); j <= min(P[i], m); j++)
{
for(int k = 1; j + k * x[i - 1] <= m; k++)
{
dp[i][j] = min(dp[i][j], dp[i - 1][j - x[i - 1]] + 1);
if(j + Y[i - 1] <= m) dp[i][j] = min(dp[i][j], dp[i - 1][j + Y[i - 1]]);
}
if(j == m)
{
for(int k = m - x[i - 1]; 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);
}
}
}
}
\(100 \,\,points\)
考虑如何把时间复杂度优化到\(O(nm)\),
首先枚举横坐标和纵坐标是难以优化的,
考虑\(x = 3\)的情况(众所周知,找规律要从\(3\)开始找)
\(dp[3,10] = min(dp[2,7] + 1,dp[2,4]+2,dp[2,1] +3)\)
\(dp[3,7] = min(dp[2,4] + 1,dp[2,1] + 2)\)
\(dp[3,4]=min(dp[2,1] + 1)\)
由以上的规律,我们很容易得出以下的一个式子:
\(dp[i][j] = min(dp[i - 1][j - x[i - 1]] + 1, dp[i][j - x[i - 1]] + 1)\)
好吧,其实不用这么麻烦,可以用从上一次转移必须装物品的完全背包进行理解
for(int i = 1; i <= n; i++)
{
for(int j = max(L[i], x[i - 1]); j <= min(P[i], m); j++)
{
dp[i][j] = min(dp[i][j], dp[i - 1][j - x[i - 1]] + 1);
dp[i][j] = min(dp[i][j], dp[i][j - x[i - 1]] + 1);
if(j == m)
{
for(int k = m - x[i - 1]; 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);
}
}
}
}
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define maxn 10100
int n, m, k;
int X[maxn], Y[maxn], L[maxn], H[maxn];
int dp[maxn][1010];
int main()
{
memset(dp, 0x3f, sizeof(dp));
scanf("%d%d%d", &n, &m, &k);
for(int i = 0; i < n; i++)
{
scanf("%d%d", &X[i], &Y[i]);
L[i] = 0; H[i] = m + 1;
} L[n] = 0; H[n] = m + 1;
for(int i = 1; i <= k; i++)
{
int x, y, z;
scanf("%d%d%d", &x, &y, &z);
L[x] = y; H[x] = z;
}
for(int i = 1; i <= m; i++) dp[0][i] = 0;
for(int i = 0; i <= n; i++)
{
for(int j = 1; j <= m; j++)
{
if(j - X[i - 1] > L[i - 1] && j - X[i - 1] < H[i - 1])
dp[i][j] = min(dp[i - 1][j - X[i - 1]] + 1, dp[i][j]);
if(j - X[i - 1] > 0)dp[i][j] = min(dp[i][j], dp[i][j - X[i - 1]] + 1);
// if(j + Y[i - 1] <= m) dp[i][j] = min(dp[i][j], dp[i - 1][j + Y[i - 1]]);
if(j == m)
{
for(int k = m - X[i - 1]; k <= m; k++)
{
if(k > L[i - 1] && k < H[i - 1])
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 - Y[i - 1]; j++)
{
if(j + Y[i - 1] > L[i - 1] && j + Y[i - 1] < H[i - 1])
dp[i][j] = min(dp[i][j], dp[i - 1][j + Y[i - 1]]);
}
}
int cnt = 0, ans = 0;
for(int i = 0; i <= n; i++)
{
// if(i == 1 || i == 2)
// printf("i = %d L = %d H = %d\n", i, L[i], H[i]);
for(int j = L[i] + 1; j <= H[i] - 1; j++)
{
// if(i == 1 || i == 2)
// printf("dp[%d][%d] = %d cnt = %d ans = %d\n", i, j, dp[i][j], cnt, ans);
if(dp[i][j] < 1e9)
{
if(cnt < i)
{
ans = 1e9; cnt = i;
ans = min(ans, dp[i][j]);
}
else ans = min(ans, dp[i][j]);
}
}
}
int tot = 0;
if(cnt != n)
{
for(int i = 0; i <= cnt; i++)
if(H[i] != m + 1) tot++;
}
if(cnt == n) printf("1\n"); else printf("0\n");
if(cnt == n) printf("%d\n", ans); else printf("%d\n", tot);
return 0;
}