P1941——飞扬的小鸟

wangyitong

2018-08-27 20:00:48

Personal

第一眼以为是搜索,看了标签,背包,dp,emm....; 状态很好设dp[i][j]//表示过了i个柱子,高度在j的最小的点击次数。那怎么转移呢?我们发现上升的时候一秒内可以点好几下,那我们可以把上升看成完全背包,下降变成01背包; 先做完全背包,再做01背包。 因为每一个时刻都有一个下界和上界,所以我们最好开两个数组up和down分别记录上界和下界; 注意dp初始化和最后统计答案 ```cpp // luogu-judger-enable-o2 #include<cstdio> #include<iostream> #include<algorithm> #include<cstring> #include<vector> #include<cmath> #include<queue> #include<map> #include<set> #define ri register int #define max maxx #define min minn using namespace std; template <class T> void in(T &x) { x = 0; bool f = 0; char c = getchar(); while (c < '0' || c > '9') { if (c == '-') f = 1; c = getchar(); } while ('0' <= c && c <= '9') { x = (x << 3) + (x << 1) + (c ^ 48); c = getchar(); } if (f) x = -x; } inline int maxx(int x,int y) { return x>y?x:y; } inline int minn(int x,int y) { return x<y?x:y; } inline int gcd(int x,int y) { return !y?x:gcd(y,x%y); } int dp[10050][1200];//飞过i个柱子,高度为j的最小点击次数; int up[12000],down[12000];//第i个位置可过的上界和下界 ; int fly[12000],drop[12000];//第i个位置点击一次可上升的高度和不点击的下降高度 int n,m,k,id,l,h,endd,tot; bool tract[12000]; int main() { memset(dp,0x3f,sizeof dp); in(n),in(m),in(k); for(ri i=0; i<n; ++i) { in(fly[i]),in(drop[i]); up[i]=m; down[i]=1; } for(ri i=1; i<=m; ++i) dp[0][i]=0;//初始化,因为他说在哪个高度开始都可以。 for(ri i=1; i<=k; ++i) { in(id),in(l),in(h); up[id]=h-1; down[id]=l+1; tract[id]=true; } for(ri i=0; i<n; ++i) for(ri j=up[i]; j>=down[i]; --j) if(dp[i][j]!=0x3f3f3f3f) { endd=i;//记录最远能飞到哪,用于统计没能到终点时,经过的柱子数。 int cnt=(m-j)/fly[i]+1; dp[i+1][j-drop[i]]=min(dp[i+1][j-drop[i]],dp[i][j]);//我们为什么要用 dp[i][j]更新dp[i+1][j-drop[i]呢,因为我们是往后推得,要用前面求过的状态,更新当前状态 for(ri tt=1; tt<=cnt; ++tt) { if(dp[i+1][min(m,j+fly[i]*tt)]>dp[i][j]+tt) dp[i+1][min(m,j+fly[i]*tt)]=dp[i][j]+tt; else break;//这句话一定得有,能剪掉好多。 } } int minm=233333333; for(int i=1; i<=m; ++i) minm=min(minm,dp[n][i]); if(minm==233333333) { for(ri i=0; i<=endd; ++i) if(tract[i]) tot++; printf("0\n%d",tot); } else printf("1\n%d",minm); return 0; } ```