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; }