luogu 1941 飞扬的小鸟
这道题对于第13个数据点,不知为什么f数组第二位开到2000以下就不能过,求指教
飞扬的小鸟
题目大意
一个小鸟在\(n*m\)的方阵里,然后有许多管道你们玩过就不多介绍了,然后每一个位置,点击会上升,不点击可以下降,点击效果可以叠加。
求如果通关的最小点击次数,否则会最多通过多少个管道。
solution
30pts
就是搜索,本以为会拿50pts。
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
#include <cstdio>
using namespace std;
int n,m,k,up[20000],down[20000],vis[20000],flag,maxn,minn=0x7fffffff;
struct edge {
int u,d;
} e[20000];
void dfs(int x,int h,int tot) {
if(h<=0) return ;
maxn=max(maxn,x);
if(x==n+1 && h>0) {
flag=true;
minn=min(minn,tot);
return;
}
if(vis[x+1]) {
for(int i=1; i<=3; i++) {
if(h+up[x]*i<e[x+1].u && h+up[x]*i>e[x+1].d )dfs(x+1,((h+up[x]*i<=m)?(h+up[x]*i):m),tot+i);
}
if(h-down[x]<e[x+1].u && h-down[x]>e[x+1].d ) dfs(x+1,h-down[x],tot);
}
else {
for(int i=1; i<=3; i++) {
dfs(x+1,((h+up[x]*i)<=m?(h+up[x]*i):m),tot+i);
}
dfs(x+1,h-down[x],tot);
}
}
int main() {
scanf("%d%d%d",&n,&m,&k);
for(int i=0; i<=n-1; i++)
scanf("%d%d",&up[i],&down[i]);
for(int i=1; i<=k; i++) {
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
vis[a]=1;
e[a].u=c;
e[a].d=b;
}
for(int i=0; i<=m; i++)
if(vis[i] && i>e[i].d && i<e[i].u)
dfs(0,i,0);
else dfs(0,i,0);
if(flag==true) {
printf("1\n%d",minn);
} else {
int ans=0;
for(int i=0; i<=maxn; i++)
if(vis[i])ans++;
printf("0\n%d",ans);
}
return 0;
}
100pts
动态规划
这道题如果细想,还真是可以用背包做,虽然限制条件多了一点。
如果小鸟向上飞,则是完全背包。
如果小鸟向下飞,则是01背包。
如果小鸟遇到柱子,那么将此状态取消
如果小鸟飞到m以上,那么定为m。
最后输出倒叙查询dp数组就可以了
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int f[10010][2010];
int n,m,k;
int x[10010],y[10010];
int vis[10010];
int low[10010],high[10010];
int main() {
scanf("%d%d%d",&n,&m,&k);
for(int i=1; i<=n; ++i) scanf("%d%d",&x[i],&y[i]);
for(int i=1; i<=n; ++i) low[i]=1,high[i]=m;
for(int i=1; i<=k; ++i) {
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
vis[a]=1;
low[a]=b+1;
high[a]=c-1;
}
memset(f,0x3f,sizeof(f));
for(int i=1; i<=m; ++i) f[0][i]=0;
for(int i=1; i<=n; ++i) {
for(int j=x[i]+1; j<=m+x[i]; ++j)
f[i][j]=min(f[i-1][j-x[i]]+1,f[i][j-x[i]]+1);//如果向上飞
for(int j=m+1; j<=m+x[i]; ++j)//将超过m的更新到m
f[i][m]=min(f[i][m],f[i][j]);
for(int j=1; j<=m-y[i]; ++j)//如果向下飞
f[i][j]=min(f[i][j],f[i-1][j+y[i]]);
for(int j=1; j<low[i]; ++j)//遇到柱子直接g
f[i][j]=f[0][0];
for(int j=high[i]+1; j<=m; ++j)
f[i][j]=f[0][0];
}
int ans=f[0][0];
for(int j=1; j<=m; ++j)
ans=min(ans,f[n][j]);
if(ans<f[0][0]) printf("1\n%d",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 p=1; p<=i; ++p)
if(vis[p])ans++;
printf("0\n%d",ans);
}
return 0;
}