【NOIP2014】飞扬的小鸟
本题在洛谷上的链接:https://www.luogu.org/problemnew/show/P1941
怎么会有这么迷的一道题!
明明思路很简单,但要想A掉,细节怎么就不好想呢!
很显然的DP,状态一目了然,处理好特殊情况,一步步推就可以了。
DP有刷表法和填表法,为了推的过程中特判是否到最高处省事,我选择了刷表法,80分,TLE了四个点,需要优化。超时主要是在于转移上,一个状态近似可以转移到m个状态,优化是当发现某次无法取得更优解,就剪枝,因为再往下肯定不会更优。而且需要从m到1枚举,可能这样会剪去更多的枝,好玄。。。
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 5 using namespace std; 6 7 inline int get_num() { 8 int num = 0; 9 char c = getchar(); 10 while (c < '0' || c > '9') c = getchar(); 11 while (c >= '0' && c <= '9') 12 num = num * 10 + c - '0', c = getchar(); 13 return num; 14 } 15 16 const int maxn = 1e4 + 5, maxm = 1e3 + 5, inf = 0x3f3f3f3f; 17 18 int tube[2][maxn], updown[2][maxn], dp[maxn][maxm]; 19 //0为下,1为上 20 21 int main() { 22 int n, m, k, ans = inf, cnta = 0, cntb = 0; 23 n = get_num(), m = get_num(), k = get_num(); 24 for (int i = 0; i < n; ++i) 25 updown[1][i] = get_num(), updown[0][i] = get_num(); 26 memset(tube[1], inf, sizeof(tube[1])); 27 for (int i = 1; i <= k; ++i) { 28 int p = get_num(), l = get_num(), h = get_num(); 29 tube[0][p] = l, tube[1][p] = h; 30 } 31 memset(dp, inf, sizeof(dp)); 32 memset(dp[0], 0, sizeof(dp[0])); 33 for (int i = 0; i <= n; ++i) 34 for (int j = m; j >= 0; --j) { 35 if (dp[i][j] == inf) continue; 36 if (i == n) {ans = min(ans, dp[i][j]); continue;} 37 cnta = max(cnta, i); 38 int next = j - updown[0][i], cnt; 39 if (next > tube[0][i + 1] && next < tube[1][i + 1]) 40 dp[i + 1][next] = min(dp[i + 1][next], dp[i][j]); 41 next = j + updown[1][i], cnt = 1; 42 if (next > m) next = m; 43 while (next <= m) { 44 if (dp[i][j] + cnt > dp[i + 1][next]) break; 45 if (next > tube[0][i + 1] && next < tube[1][i + 1]) 46 dp[i + 1][next] = min(dp[i + 1][next], dp[i][j] + cnt); 47 if (next == m) break; 48 next += updown[1][i], ++cnt; 49 if (next > m) next = m; 50 } 51 } 52 if (ans < inf) printf("1\n%d", ans); 53 else { 54 for (int i = 0; i <= cnta; ++i) 55 if (tube[1][i] < inf) ++cntb; 56 printf("0\n%d", cntb); 57 } 58 return 0; 59 }