(advanced) UVA 10949 Kids in a Grid

Problem A
Kids in a Grid
Input:
 standard input
Output: standard output
 

Two kids are walking in a H x W grid. Each square in the grid contains a character (whose ASCII code lies between 33 and 127). Both kids can move north, east, west and south each step. The first kid walked N steps, the second kid walked M steps. (0 ≤ N ≤ M ≤ 20000).
 
If we write down all the characters each kid walks on, we get two strings SA and SB. your task is to delete as few characters as possible, so that the two new strings are the same.  
 
Input

the first line contains a single integer t(1 ≤ t ≤ 15), the number of test cases. Each test case contains several lines. The first line contains two integers H and W (1 ≤ H, W ≤ 20), the next H lines contains the grid. Next line contains three integers NX0 and Y0 (1 ≤ X0 ≤ H1 ≤ Y0 ≤ WX increases from North to South, while Y increases from West to East), indicating the first kinds walks from (X0, Y0), for N steps. The next line contains a string of N characters, NEWS stands for North, West, South and East, respectively. The second kid's information follows, which is the same format.

 

You may assume the walk sequence is correct: they will never go outside the grid.

 

Output

For each case, print the case number and two integers XA and XB, indicating the number of characters deleted from SA and SB, respectively. 

 

Sample Input                               Output for Sample Input

2

3 4

ABCD

DEFG

ABCD

4 1 1

EEES

3 3 1

NES

3 4

ABCD

DEFG

ABCD

4 1 1

EEES

3 3 1

NES

Case 1: 3 2

Case 2: 3 2 


Problem setter: Rujia Liu, EPS

[note]

in the first sample, SA = ABCDGSB = ADEB, we must delete 3 characters from SA and 2

from SB, so that they are the same (both AB or AD)




题意:相当于是给出两个字符串,删掉尽可能少的字母,使得两个字符串一模一样。


思路:其实这道题目就是要求LCS,只要求出最长公共字串,把公共部分留下,其余删除,这显然是最优的。 假设不是,也就是说某个字符串还至少能少删除一个,这个不删除,那么就一定对应另一个字符串的一个字母,但是这样刚才求出来的就不是LCS,矛盾。 直接用O(nm)的算法是会超时的,你可以选择去看论文.....我自己根据黑书说的写了个凑合的优化吧。 在常规的动态规划中,我们有三个转移方程,dp[i-1][j],dp[i][j-1]以及 dp[i][j]=dp[i-1][j-1]+1(A[i]==B[j]) 注意到,只有A[i]==B[j]的时候值才有可能变大。dp[i-1][j],dp[i][j-1] 这两个转移的作用无非就是把最大值从前面传到后面而已。 所以我们不必所有的点都考虑。我们只考虑那些A[i]==B[j]的点对,那么怎么更新呢?我们要在dp[i'][j'] (i'<i,j'<j) 中找到一个最大值。 如果我们有一个线段树保存了每个节点保存了第二维是j'的dp的最大值,那么我们就能在线段树的[1,j-1]里面找到最大值,然后将其+1. 当i对应都更新完了以后,我们在更新线段树。 这种做法,有时候比常规的动态规划还要慢的。。。因为最坏是O(nmlgm)。


代码:

#include <vector>
#include <list>
#include <map>
#include <set>
#include <queue>
#include <string.h>
#include <string>
#include <deque>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <ctime>
using namespace std;
#define mp make_pair
#define rep(i,n) for(int i=0;i<n;++i)
#define clr(a,x) memset(a,x,sizeof(a))
#define LL long long

inline int max(int a,int b) { return a>b?a:b; }
inline int min(int a,int b) { return a<b?a:b; }

const int N=20000+5;
vector<int> v[100];
int mx[N<<2],tree_size;

void update(int p,int x)
{
    p+=tree_size; if(mx[p]>=x) return;
    mx[p]=x; p>>=1;
    while(p>=1) {
        int k=max(mx[p<<1],mx[p<<1|1]);
        if(k>mx[p]) mx[p]=k;
        else break;
        p>>=1;
    }
}

int query(int l,int r)
{
    int ret=0;
    l+=tree_size; r+=tree_size;
    while(l<r) {
        if(l&1) { ret=max(ret,mx[l]); ++l; }
        if(~r&1) { ret=max(ret,mx[r]); --r; }
        l>>=1; r>>=1;
    }
    if(l==r) ret=max(ret,mx[l]);
    return ret;
}

int h,w,n,m;
char sa[N],sb[N],op[N];
char maze[30][30];

void input()
{
    for(int i=0;i<h;++i) scanf("%s",maze[i]);
    scanf("%d",&n); int r,c;
    gets(op);
    sscanf(op,"%d %d",&r,&c); --r; --c;
    gets(op);
    for(int i=0;i<n;++i) {
        sa[i]=maze[r][c];
        if(op[i]=='E') ++c;
        else if(op[i]=='S') ++r;
        else if(op[i]=='W') --c;
        else if(op[i]=='N') --r;
    }
    sa[n]=maze[r][c]; sa[++n]=0;
    scanf("%d",&m);
    gets(op); sscanf(op,"%d %d",&r,&c);
    --r; --c;
    gets(op);
    for(int i=0;i<m;++i) {
        sb[i]=maze[r][c];
        if(op[i]=='E') ++c;
        else if(op[i]=='S') ++r;
        else if(op[i]=='W') --c;
        else if(op[i]=='N') --r;
    }
    sb[m]=maze[r][c]; sb[++m]=0;
    for(int i=0;i<100;++i) v[i].clear();
    for(int i=0;i<m;++i) {
        v[sb[i]-33].push_back(i+1);
    }
}

int stk[N],value[N],c;
void solve()
{
    clr(mx,0);
    tree_size=1; while(tree_size<m) tree_size<<=1;
    tree_size--;
    int ans=0;
    for(int i=0;i<n;++i) {
        int z=sa[i]-33;
        c=0;
        for(int j=0;j<v[z].size();++j) {
         //   printf("%d ",v[z][j]);
            int x=query(1,v[z][j]-1);
            if(x+1>ans) ans=x+1;
            stk[c]=v[z][j]; value[c++]=x+1;
        }
        for(int j=0;j<c;++j) update(stk[j],value[j]);
    }
    printf("%d %d\n",n-ans,m-ans);
}

int main()
{
    int T;cin>>T; int Cas=0;
    while(T--) {
        ++Cas;
        scanf("%d%d",&h,&w);
        input();
        printf("Case %d: ",Cas);
        solve();
    }
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值