首先,这是一道 dp 题
题目的大意是让我们求 能完成游戏的最少点击屏幕数 或不能完成游戏时通过最多的水管。
其实就是求一个重叠的子问题:我们的小鸟最远能飞到哪?此时点击的屏幕数是多少?
然后,又可以发现,如果到达一个位置点击屏幕数最少时,从该位置到达下一位置,点击屏幕数也是最少的,这就是最优子结构性质。
同时已求解问题不会影响之后问题的求解,即无后效性。所以该问题已经满足使用 dp 情况了。
于是我们用 dp 来求解
我们设数组 f[][] , f[i][j] 表示到 (i,j) 点击最少屏幕数 ,若不能到达 , f[i][j] = INF (无穷大)
接着我们思考如何求出状态转移方程
我们用贪心的思想很容易就能得到最优决策,就是一个位置应从点击屏幕数少的位置转移而来
那么 (i,j) 位置的转移只可能有两种情况:
1) 上升转移:
----① 从 i-1 轴转移而来
----② 从 i 轴转移而来
2) 下降转移 (自由落体)
然后,再来处理水管的问题
1) 水管实体不可触,那么将其坐标全赋值为 INF
2) 在不能完成游戏的情况下,求解通过的最多水管数可以先求能到达的最远横坐标,再枚举求解
如果没看懂,代码里有详细的状态转移方程解释
代码实现
/*
f[i][j] 表示到坐标 (i,j) 最少需要跳的次数
如果这个点不能到达,就把它的值赋为正无穷,设 INF=正无穷
f[i][j] 的状态转移:
上升转移:
1. (i,j) 由x轴的 i-1 转移而来
方程: f[i][j+x[i]]=f[i-1][j]+1 j=1,2,3,......m
2. (i,j) 由x轴的 i 转移过来
y未超过m: f[i][j+x[i]]=f[i][j]+1 j=1,2,3,......m
y超过m: f[i][m]=min(f[i][j],f[i][m]) j=m+1,m+2,......m+x[i]
总:
f[i][j+x[i]]=min(f[i-1][j]+1,f[i][j]+1) j=1,2,3,......m
f[i][m]=min(f[i][j],f[i][m]) j=m+1,m+2,......m+x[i]
下降转移:
方程: f[i][j+y[i]]=min(f[i][j],f[i-1][j]) j=1,2,3,......m
不能通过:
我们给每个x坐标赋予一个最低通过值 low[x]
那么 f[i][j]=INF j<low[i]
同样的,给每个x坐标赋予一个最高通过值 high[x]
那么 f[i][j]=INF j>high[i]
*/
#include<bits/stdc++.h>
using namespace std;
const int N=10000+10;
int n,m,k,ans;
struct node
{
int x,y,hi,lo;
//上升高度x,下降高度y ,最低通过值lo,最高通过值hi
bool falg; //判断有无管道
}p[N];
int f[N][2010];
int main()
{
memset(f,0x3f,sizeof(f));
scanf("%d%d%d",&n,&m,&k);
for(int i=0;i<=n;i++) p[i].lo=1,p[i].hi=m;
for(int i=1;i<=n;i++) scanf("%d%d",&p[i].x,&p[i].y);
for(int i=1;i<=k;i++)
{
int P,L,H;
scanf("%d%d%d",&P,&L,&H);
p[P].falg=1,p[P].lo=L+1,p[P].hi=H-1;
}
/*for(int i=1;i<=n;i++){
printf("%d %d %d\n",p[i].falg,p[i].lo,p[i].hi);
}*/
for(int i=1;i<=m;i++) f[0][i]=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
f[i][j+p[i].x]=min(f[i-1][j]+1,f[i][j]+1);
for(int j=m+1;j<=m+p[i].x;j++)
f[i][m]=min(f[i][j],f[i][m])/*,printf("%d ",f[i][j])*/;
for(int j=1;j<=m-p[i].y;j++)
f[i][j]=min(f[i][j],f[i-1][j+p[i].y]);
for(int j=1;j<p[i].lo;j++)
f[i][j]=f[0][0];
for(int j=p[i].hi+1;j<=m;j++)
f[i][j]=f[0][0];
/*for(int u=m;u>=0;u--){
for(int v=0;v<=n;v++){
printf("%d ",f[v][u]);
}
printf("\n");
}*/
}
ans=f[0][0];
for(int i=1;i<=m;i++) ans=min(ans,f[n][i]);
if(ans<f[0][0]) printf("1\n%d\n",ans); //先判断能否完成游戏
else //不能完成游戏,就先求能到达的最远横坐标,再求通过最多的水管数
{
int i,j;
for(i=n;i>=1;i--)
{
for(j=1;j<=m;j++)
{
if(f[i][j]<f[0][0]) break;
}
if(j<=m) break;
}
ans=0;
for(int l=1;l<=i;l++)
{
if(p[l].falg) ans++;
}
printf("0\n%d\n",ans);
}
return 0;
}
题解很好,只不过lz AFO了.