【Kickstart】2018 Round D - Funniest Word Search

解法

第一步,分别统计以(i,j)结尾的,长度不超过k的单词总长度,横着和竖着分别记作h[i,j,k]v[i,j,k]

然后我们考虑DP,状态为f[i1,j1,i2,j2]
四重循环遍历的时候:

  1. i2每增加1的时候,总是从f[i1,j1,i2,j1]开始的,这是竖着的一列,所以我们需要知道这一列里:

    1. 横着,以区间(i1,j1)(i2,j1)之间的字符结尾,长度不超过1的单词总长度,假设这部分为A
    2. 竖着,在区间(i1,j1)(i2,j1)之间的单词总长度,假设这部分为B

    那么有初始条件:f[i1,j1,i2,j1]=A+B

  2. i2固定,j2每增加1的时候,相当于与f[i1,j1,i2,j1-1]相比增加了竖着的一列,与上一条相似地,我们需要增加:

    1. 横着,以区间(i1,j2)(i2,j2)之间的字符结尾,长度不超过j2-j1+1的单词总长度,假设这部分为C
    2. 竖着,在区间(i1,j2)(i2,j2)之间的单词总长度,假设这部分为D

    那么递推方程为:f[i1,j1,i2,j2]=f[i1,j1,i2,j2-1]+C+D

如何求第一部分呢?我们可以对h数组,在固定j,k的情况下,对i轴求前缀和,记作cumv[i,j,k]=cumv[i-1,j,k]+h[i,j,k],这样就有:
A = cumv[i2,j1,1]-cumv[i1-1,j1,1]
C = cumv[i2,j2,j2-j1+1]-cumv[i1-1,j2,j2-j1+1]

对于第二部分,我们可以用cumh[i,j,k]记录区间(i-k+1,j)(i,j)之间的单词总长度,它可以这么求:
cumh[i,j,k] = cumh[i-1,j,k-1]+v[i,j,k]
这样就有:
B = cumh[i2,j1,i2-i1+1]
D = cumh[i2,j2,i2-i1+1]

注意所有的数组都用int会超内存限制,我们可以分析一下:

  1. 数组vh最多是1000个单词长度为100,甚至题目还有条件限制所有单词长度加起来不超过5000,所以它们的值最大也不会超过10**4(假如正逆要算两遍的话)
  2. 然后cumvcumh相当于是对某一维进行累加,所以多100倍,即10**6
  3. 然后是f,又进行了累加,所以大约再涨100倍,即10**8

可见3个都不会超过int,但是,在比较分数大小的时候,如果像我一样用的是乘法而不是除法,那么中间结果有可能超过int,需要转换成long long

#include <stdio.h>
#include <string>
#include <iostream>
#include <memory.h>
#include <stdlib.h>
#include <unordered_set>
#include <unordered_map>
#include <cmath>
#include <vector>
#include <algorithm>

#define MAXN 110
#define NINF 0x80000000


using namespace std;

typedef long long lld;
typedef unordered_multiset<string> DICT;

string matrix[MAXN];
unordered_map<int,DICT> words;
int v[MAXN][MAXN][MAXN],h[MAXN][MAXN][MAXN];
int cumv[MAXN][MAXN][MAXN],cumh[MAXN][MAXN][MAXN];
int f[MAXN][MAXN][MAXN][MAXN];
int r,c,w;
lld score,size,cnt;

void insert(string &word) {
    int l = word.size();
    if(words.find(l)==words.end())
        words[l] = DICT();
    words[l].insert(word);
}

void reduce(lld &a,lld &b) {
    lld x=a,y=b;
    if(x<y) {
        x^=y;y^=x;y^=x;
    }
    while(x%y!=0) {
        int tmp = y;
        y = x%y;
        x = tmp;
    }
    a /= y,b/=y;
}

void newVal(int i1,int j1,int i2,int j2) {
    lld s = i2-i1+j2-j1+2;
    lld sc = f[i1][j1][i2][j2];
    if(sc*size>=score*s) {
        if(sc*size==score*s) cnt++;
        else {
            cnt = 1;
            score = f[i1][j1][i2][j2];
            size = s;
            reduce(score,size);
        }
    }
}

void solve() {
    score = 0, size = 1, cnt = 0;
    memset(v,0,sizeof(int)*MAXN*MAXN*MAXN);
    memset(h,0,sizeof(int)*MAXN*MAXN*MAXN);
    memset(cumv,0,sizeof(int)*MAXN*MAXN*MAXN);
    memset(cumh,0,sizeof(int)*MAXN*MAXN*MAXN);
    for(int i=1;i<=r;++i)
        for(int j=0;j<c;++j) {
            string ver="";
            for(int k=1;k<=i;++k) {
                v[i][j][k] = v[i][j][k-1];
                ver += matrix[i-k+1][j];
                if(words.find(k)!=words.end()) {
                    DICT &dict = words[k];
                    v[i][j][k]+=dict.count(ver)*k;
                }
            }
            string hor="";
            for(int k=1;k<=1+j;++k) {
                h[i][j][k] = h[i][j][k-1];
                hor += matrix[i][j-k+1];
                if(words.find(k)!=words.end()) {
                    DICT &dict = words[k];
                    h[i][j][k]+=dict.count(hor)*k;
                }
            }
        }

    for(int i=1;i<=r;++i)
        for(int j=0;j<c;++j) {
            for(int k=1;k<=1+j;++k) {
                cumv[i][j][k] = cumv[i-1][j][k]+h[i][j][k];
            }
            for(int k=1;k<=i;++k) {
                cumh[i][j][k] = cumh[i-1][j][k-1]+v[i][j][k];
            }
        }

    memset(f,0,sizeof(int)*MAXN*MAXN*MAXN*MAXN);
    for(int i1=1;i1<=r;++i1)
        for(int j1=0;j1<c;++j1) {
            for(int i2=i1;i2<=r;++i2) {
                f[i1][j1][i2][j1] = cumh[i2][j1][i2-i1+1]+cumv[i2][j1][1]-cumv[i1-1][j1][1];
                newVal(i1,j1,i2,j1);
                for(int j2=j1+1;j2<c;++j2) {
                    f[i1][j1][i2][j2] = f[i1][j1][i2][j2-1]+cumh[i2][j2][i2-i1+1]+cumv[i2][j2][j2-j1+1]-cumv[i1-1][j2][j2-j1+1];
                    newVal(i1,j1,i2,j2);
                }
            }
        }
}

int main() {
//    freopen("obj.txt","r",stdin);
//    freopen("my.out","w",stdout);
    int t;
    scanf("%d",&t);
    for(auto round=1;round<=t;++round) {
        words.clear();
        scanf("%d%d%d\n",&r,&c,&w);
        for(int i=1;i<=r;++i)
            getline(cin,matrix[i]);
        string str;
        for(int i=0;i<w;++i) {
            getline(cin,str);
            insert(str);
            reverse(str.begin(),str.end());
            insert(str);
        }
        solve();
        reduce(score,size);
        printf("Case #%d: %lld/%lld %lld\n",round,score,size,cnt);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值