NOI2005 瑰丽华尔兹
题目链接:戳我
先化简一下题意:一共n次操作,每次按照给定方向可以走[0,r-l+1]个格子,障碍物格子不能走,问到最后最大的路径长度为多少?
60pts做法:
设\(dp[i][j][k]\)表示在执行完第k段的操作之后,的最大答案。
\(dp[i][j][k]=max(dp[i][j][k],dp[x][y][k-1]+calc(i,j,x,y))\)
其中x,y为可以通过第k次操作到达i,j的格子。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#define MAXN 210
using namespace std;
int n,m,st_x,st_y,K;
int dp[MAXN][MAXN][MAXN];
char s[MAXN][MAXN];
struct Node{int op,l,r,len;}node[MAXN];
inline int calc(int a,int b)
{
if(a<b) swap(a,b);
return a-b;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("ce.in","r",stdin);
// freopen("ce.out","w",stdout);
#endif
scanf("%d%d%d%d%d",&n,&m,&st_x,&st_y,&K);
for(int i=1;i<=n;i++) scanf("%s",s[i]+1);
for(int i=1;i<=K;i++)
{
scanf("%d%d%d",&node[i].l,&node[i].r,&node[i].op);
node[i].len=node[i].r-node[i].l+1;
}
memset(dp,-0x3f,sizeof(dp));
dp[st_x][st_y][0]=0;
for(int k=1;k<=K;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
if(node[k].op==4)
{
int x=i;
for(int y=j;y>=max(1,j-node[k].len);y--)
{
if(s[x][y]=='x') break;
dp[i][j][k]=max(dp[i][j][k],dp[x][y][k-1]+calc(j,y));
}
}
else if(node[k].op==3)
{
int x=i;
for(int y=j;y<=min(m,j+node[k].len);y++)
{
if(s[x][y]=='x') break;
dp[i][j][k]=max(dp[i][j][k],dp[x][y][k-1]+calc(j,y));
}
}
else if(node[k].op==1)
{
int y=j;
for(int x=i;x<=min(n,i+node[k].len);x++)
{
if(s[x][y]=='x') break;
dp[i][j][k]=max(dp[i][j][k],dp[x][y][k-1]+calc(i,x));
}
}
else if(node[k].op==2)
{
int y=j;
for(int x=i;x>=max(1,i-node[k].len);x--)
{
if(s[x][y]=='x') break;
dp[i][j][k]=max(dp[i][j][k],dp[x][y][k-1]+calc(i,x));
}
}
}
int ans=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
ans=max(ans,dp[i][j][K]);
printf("%d\n",ans);
return 0;
}
100pts做法
就是我们发现最内层转移是可以利用单调队列优化,把\(O(n^2)\)转化成\(O(n)\)的
然后就星了
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define MAXN 210
#define INF 0x3f3f3f3f
using namespace std;
int n,m,X,Y,K,ans;
int f[2][MAXN][MAXN],q[MAXN];
char s[MAXN][MAXN];
struct Node{int l,r,op,len;}node[40010];
int main()
{
#ifndef ONLINE_JUDGE
freopen("ce.in","r",stdin);
#endif
scanf("%d%d%d%d%d",&n,&m,&X,&Y,&K);
for(int i=1;i<=n;i++) scanf("%s",s[i]+1);
for(int i=1;i<=K;i++) scanf("%d%d%d",&node[i].l,&node[i].r,&node[i].op);
for(int i=1;i<=K;i++) node[i].len=node[i].r-node[i].l+1;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
f[0][i][j]=-INF;
f[0][X][Y]=0;
int now=1,pre=0;
for(int k=1;k<=K;k++,now^=1,pre^=1)
{
if(node[k].op==1)
{
for(int j=1;j<=m;j++)
{
int head=1,tail=0;
for(int i=n;i>=1;i--)
{
if(s[i][j]=='x'){head=1,tail=0;continue;}
while(head<=tail&&q[head]-i>node[k].len) head++;
while(head<=tail&&f[pre][q[tail]][j]+q[tail]-i<=f[pre][i][j]) tail--;
q[++tail]=i;
f[now][i][j]=f[pre][q[head]][j]+q[head]-i;
}
}
}
else if(node[k].op==2)
{
for(int j=1;j<=m;j++)
{
int head=1,tail=0;
for(int i=1;i<=n;i++)
{
if(s[i][j]=='x'){head=1,tail=0;continue;}
while(head<=tail&&i-q[head]>node[k].len) head++;
while(head<=tail&&f[pre][q[tail]][j]+i-q[tail]<=f[pre][i][j]) tail--;
q[++tail]=i;
f[now][i][j]=f[pre][q[head]][j]+i-q[head];
}
}
}
else if(node[k].op==3)
{
for(int i=1;i<=n;i++)
{
int head=1,tail=0;
for(int j=m;j>=1;j--)
{
if(s[i][j]=='x') {head=1,tail=0;continue;}
while(head<=tail&&q[head]-j>node[k].len) head++;
while(head<=tail&&f[pre][i][q[tail]]+q[tail]-j<=f[pre][i][j]) tail--;
q[++tail]=j;
f[now][i][j]=f[pre][i][q[head]]+q[head]-j;
}
}
}
else
{
for(int i=1;i<=n;i++)
{
int head=1,tail=0;
for(int j=1;j<=m;j++)
{
if(s[i][j]=='x') {head=1,tail=0;continue;}
while(head<=tail&&j-q[head]>node[k].len) head++;
while(head<=tail&&f[pre][i][q[tail]]+j-q[tail]<=f[pre][i][j]) tail--;
q[++tail]=j;
f[now][i][j]=f[pre][i][q[head]]+j-q[head];
}
}
}
}
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
ans=max(ans,f[pre][i][j]);
printf("%d\n",ans);
return 0;
}