[NOIp2014] 飞扬的小鸟
传送门:>Here<
很经典的题目,题意有些难描述,见原题。
解题思路
这是一个多阶段决策问题的模型,那么可以考虑$DP$。对于每一个位置$dp_{i,j}$,可以是下落到达的,从$dp_{i-1,j+y_i}$转移来。可以是跳上来的,从$dp_{i-1,j-k*x_i}(k \geq 1)$转移来。
这两个方程就是很标准的背包方程。前者是01背包,后者是完全背包。于是我们就想到了完全背包的优化。就在这里是个难点,题目考察了对完全背包优化的理解。
完全背包为什么可以不用枚举$k$?优化,其实就是尽量少作重复工作,尽量不做无用工作。在完全背包中,对于$dp_{i,j}$可以从$dp_{i-1,j-k*x_i}(k \geq 0)$转移而来,而$dp_{i,j-x_i}$是从$dp_{i-1,j-x_i-k*x_i}(k \geq 0)$转移而来的。这之间就有重复工作了。于是我们直接把$dp_{i,j-x_i}$继承下来,在加上这一轮的转移就行了。
这里是一样的。只不过$k$的取值不同,因此继承还是继承,只不过这一轮的转移改变了。而很多人完全背包的滚动版本打多了,忽略了完全背包的滚动版本也是有这一轮自己的转移的,只不过这一轮是继承$dp_{i-1,j}$,而因为滚动,这个值本身就在那里,代码里就不写了。
这道题还加进了状态不存在的情况。我们不能因为某一状态不存在就不去做了,因为后面的有可能要从这个位置转移。处理方法是一列做完后手动去除不合法状态。另外,天花板的情况需要暴力转移。
$Code$
/*DennyQi 2019*/ #include <cstdio> #include <algorithm> #include <cstring> #include <queue> #include <cmath> const int N = 10010; const int P = 998244353; const int INF = 0x3f3f3f3f; inline int mul(const int& a, const int& b){ return 1ll*a*b%P; } inline int add(const int& a, const int& b){ return (a+b>=P)?a+b-P:a+b; } inline int sub(const int& a, const int& b){ return (a-b<0)?a-b+P:a-b; } inline int read(){ int x(0),w(1); char c = getchar(); while(c^'-' && (c<'0' || c>'9')) c = getchar(); if(c=='-') w = -1, c = getchar(); while(c>='0' && c<='9') x = (x<<3)+(x<<1)+c-'0', c = getchar(); return x*w; } int n,m,pp,ans1,ans2,p,cnt,x[N],y[N],l[N],h[N],cp[N],dp[N][1000]; int main(){ freopen("file.in","r",stdin); n = read(), m = read(), pp = read(); for(int i = 1; i <= n; ++i){ l[i] = 0; h[i] = m+1; } for(int i = 1; i <= n; ++i){ x[i] = read(), y[i] = read(); } for(int i = 1; i <= pp; ++i){ p = read(); cp[p] = 1; l[p] = read(); h[p] = read(); } for(int i = 1; i <= n; ++i){ for(int j = 1; j < m; ++j){ dp[i][j] = INF; if(j-x[i] > 0){ dp[i][j] = std::min(dp[i][j],std::min(dp[i][j-x[i]]+1,dp[i-1][j-x[i]]+1)); // printf("%d,%d(%d) => %d,%d\n",i,j-x[i],dp[i][j-x[i]],i,j); } } for(int j = 1; j < m; ++j){ if(j+y[i] <= m){ dp[i][j] = std::min(dp[i][j],dp[i-1][j+y[i]]); } } dp[i][m] = INF; if(i == 1){ dp[i][m] = 1; }else{ for(int j = m; j >= 1; --j){ dp[i][m] = std::min(dp[i][m],dp[i-1][j]+((j==m)?1:(int)std::ceil((m-j)*1.0/x[i]))); // printf("%d+%d\n",dp[i-1][j],(j==m)?1:(int)std::ceil((m-j)*1.0/x[i])); } } for(int j = 1; j <= l[i]; ++j){ dp[i][j] = INF; } for(int j = h[i]; j <= m; ++j){ dp[i][j] = INF; } for(int j = 1; j <= m; ++j){ // printf("dp[%d][%d] = %d\n",i,j,dp[i][j]); if(dp[i][j] < INF){ ans2 = std::max(ans2,i); } } } if(ans2 < n){ printf("0\n"); for(int i = 1; i <= ans2; ++i){ if(cp[i]){ ++cnt; } } printf("%d\n",cnt); }else{ printf("1\n"); ans1 = INF; for(int i = 1; i <= m; ++i){ ans1 = std::min(ans1,dp[n][i]); } printf("%d\n",ans1); } return 0; }