hdu 6076 Security Check
题
OvO http://acm.hdu.edu.cn/showproblem.php?pid=6076
2017 Multi-University Training Contest - Team 4 - 1010
解
O(n2)的DP的话,
第二种情况,dp[i][j]=max(max(dp[i][j-1],dp[i-1][j])+1,dp[i-1][j-1]+1),这种情况下dp[i][j]是必然取dp[i-1][j-1]+1的,
这样对于dp[n][n]就可以类似贪心地处理,每次搜索碰到第二种情况优先取第二种情况,否则就取第一种的最大值。
而在第一种状况下,因为k很小,所以要搜索的状态其实是很少的。
对于第二种状况,拿一个向量记录每条对角线上不符合状况二的情况,这样每次搜到第二种情况都可以跳跃着搜索,(在起跳点所在的这条对角线的向量上,对跳跃的落脚点进行二分)。
这样总的情况其实是很少的,可以加一个记忆化
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <map>
#include <vector>
using namespace std;
typedef long long ll;
const int M=6e4+44;
const int bas=M-22;
int n,k;
int A[M],B[M],plcb[M];
map<int,map<int,int> > mp; //saves for case 1,case 2
vector<int> dia[M*2]; //diagonals saves
void init()
{
mp.clear();
int i,j,valb,pb;
for(i=1-n;i<=n-1;i++)
dia[i+bas].clear();
for(i=1;i<=n;i++)
for(j=0;j<=k;j++)
{
valb=A[i]-j;
if(valb>0)
{
pb=plcb[valb];
dia[i-pb+bas].push_back(i);
}
valb=A[i]+j;
if(valb<=n)
{
pb=plcb[valb];
dia[i-pb+bas].push_back(i);
}
}
for(i=1-n;i<=n-1;i++)
sort(dia[i+bas].begin(),dia[i+bas].end());
}
int deal(int wh,int now)
{
int i,j,li,ri,mid;
if(dia[wh].size()==0 || dia[wh][0]>now)
return now-min(now,now-(wh-bas));
li=0; ri=dia[wh].size();
while(li<ri-1)
{
mid=(li+ri)>>1;
if(dia[wh][mid]<now)
li=mid;
else
ri=mid;
}
return dia[wh][li];
}
int dfs(int x,int y)
{
int i,j,tmp,ret,val1,val2;
if(x==0 || y==0)
return x+y;
if(abs(A[x]-B[y])<=k)
{
val1=mp[x-1][y];
if(val1==0) val1=dfs(x-1,y);
val2=mp[x][y-1];
if(val2==0) val2=dfs(x,y-1);
ret=1+min(val1,val2);
mp[x][y]=ret;
return ret;
}
else
{
tmp=x-deal(x-y+bas,x);
ret=tmp+dfs(x-tmp,y-tmp);
mp[x][y]=ret;
return ret;
}
}
int main()
{
// freopen("数据\\1010.in","r",stdin);
// freopen("数据\\fxxl1010.out","w",stdout);
int i,j;
int cas;
scanf("%d",&cas);
while(cas--)
{
scanf("%d%d",&n,&k);
for(i=1;i<=n;i++)
scanf("%d",&A[i]);
for(i=1;i<=n;i++)
{
scanf("%d",&B[i]);
plcb[B[i]]=i;
}
init();
int ans=dfs(n,n);
printf("%d\n",ans);
}
return 0;
}

浙公网安备 33010602011771号