【动态规划】Phone Numbers

最近开始刷Ural,这三天先做了1000~1005,和十多道水题都算不上的题。。。

Ural 1002

Background

现实生活中,你时常会遇到许多许多而且越来越长的电话号码。你需要记住这类型的号码。 例如按下面的图示,把字母划分到特定的数字上,是一种很容易就能把数字记住的方法:

	1 ij	2 abc	3 def
	4 gh	5 kl	6 mn
	7 prs	8 tuv	9 wxy
	0 oqz

按这种方法:每个字或一个词组可被代替成一组特定的数字,那么,你只可以通过记住一些词就能记住相应电话号码。 如果可以找出一种单词与个人电话号码的简单关系,它是很有吸引力的。例如你的一个棋友的电话号码是941837296,你可以用 WHITEPAWN来代替;又如你可以用BULLDOG来代替你的一个喜爱的老师的电话号码:2855304。

[编辑] Problem

对给定的给定的数字和单词表,求出一个最简短的单词序列(也就是得出一尽可能短的单词来代替相应的数字)。这种对应关系要求符合上图所描述的关系。


[编辑] Input

输入包含若干组的测试数据。每组测试点的第一行是你所要记住的电话号码。这个号码最多有100个数位。测试的第二行是单词总数(最大为50000个)。以下的每一行是只包含一个单词,单词长度最大限制为50个字母。整个输入文件的大小不超过300KB。 输入文件的最后一行以-1作为结束标志。


[编辑] Output

输出文件的每一行为找到的最短单词序列。每个单词间用一个空格隔开。如果没有解决方案,则输出“No solution.”。 如果有多个单词满足条件,可以从中选择任一个单词输出。


[编辑] Sample Input

7325189087
5
it
your
reality
real
our
4294967296
5
it
your
reality
real
our
-1


[编辑] Sample Output

reality our
No solution.


一开始用的三维时间,二维空间的算法,不知是如何想的,后来改成O(n^2)的了。

很简单的一道题,有人把它看作是背包,但是我觉得完全没有必要再把这样的题还映射到某个模型了。


f[i] = min(f[i - len[j]]+1) if(NUM[i-len[j],i] == WRD[j])


考虑到字母到数字的映射是多对一的,因此我们就映射字母到数字。

另外,本题要注意边界。就是f[len[j]] = 1 if(NUM[len[j]] == WRD[j])

还有要注意,这道题最好将转移数组f的下界从0开始,和这个string的下界同步-_-b,同时也因为这个原因,要单独进行以上的初始化,否则,直接从f[0]转移过来就好了,不用单独初始化


#include <iostream>
#include <algorithm>
#include <cstdio>
#include <string>
using std::min;
using std::string;
using std::cout;
using std::cin;
string PN;
string wrds[50010];
string wrds2[50010];
char chrctr[26] = {'2','2','2','3','3','3','4','4','1','1','5','5','6','6','0','7','0','7','7','8','8','8','9','9','9','0'};
long length[50010];
long f[50010];
long g[50010];
long pnlen;
long n;

void dp()
{
    for (long i=0;i<pnlen;i++)
    {
        g[i] = 0;
        f[i] = 0x7f7f7f7f;
    }
    for (long i=1;i<n+1;i++)
    {
        if (PN.compare(0,length[i],wrds[i],0,length[i])==0)
        {
            f[length[i]-1] = 1;
            g[length[i]-1] = i;
        }
    }
    for (long i=1;i<pnlen;i++)
    {
        for (long j=1;j<n+1;j++)
        {
            if (i-length[j]>=0 && PN.compare(i-length[j]+1,length[j],wrds[j],0,length[j])==0)
            {
                if (f[i-length[j]]+1 < f[i])
                {
                    f[i] = f[i-length[j]]+1;
                    g[i] = j;
                }
            }
        }
    }
}

void ou(long l)
{
    if (g[l] == 0)return;
    ou(l-length[g[l]]);
    cout << wrds2[g[l]] << " ";
}

void translate(long l)
{
    for (long i=0;i<length[l];i++)
    {
        wrds[l][i] = chrctr[wrds[l][i]-'a'];
    }
}

int main()
{
    freopen("phone.in","r",stdin);
    freopen("phone.out","w",stdout);
    while (1)
    {
        cin >> PN;
        if (PN == "-1") break;
        pnlen = PN.length();
        cin >> n;

        for (long i=1;i<n+1;++i)
        {
            cin >> wrds[i];
            wrds2[i] = wrds[i];
            length[i] = wrds[i].length();
            translate(i);
        }
        dp();
        if (f[pnlen-1]<0x7f7f7f7f) ou(pnlen-1);
        else cout << "No solution.\n";
        cout << "\n";
    }
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值