2020杭电HDU-6831多校第六场Fragrant numbers(区间DP打表)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6831
博客园食用链接:https://www.cnblogs.com/lonely-wind-/p/13461891.html

Problem Description
Many people love numbers, and some have a penchant for specific numbers. Nowadays in popular culture, 1145141919 1145141919 is a very fragrant number, and many people want to represent all other numbers with this number.

Let S be an infinite string of " 1145141919 " "1145141919" infinitely stitched together as “ 114514191911451419191145141919... 114514191911451419191145141919... ”.

Take a prefix T of S , you can insert ‘(’ , ‘)’ , ‘+’ or ‘∗’ to T to form a new string T′, and then let the value of T′ be val(T′) according to ordinary rules. (You can insert any number of operators, even 0. But need to ensure that the inserted operators form legitimate operations)

Now for a number N, please calculate the minimum length of T that can make val(T′)=N. For example, when N=520, the minimum length of 6 (pick the first 6 characters 114514 and insert operators to make T′=1+1+4+514 , then we have val(T′)=520 )

If no such T exists, output −1.

Input
There are multiple test cases.

The first line with a number t indicates the number of test cases.

For each test case, one integer N per line indicates an inquiry.
1 t 30 1≤t≤30
1 N 5000 1≤N≤5000

Output
Output t lines.
One integer per line indicates the corresponding answer.

Sample Input
3
520
1
2
Sample Output
6
1
2

题目大意:给你一个字符串 s = 1145141919 s=1145141919 ,它可以无限循环下去,现在你要选择一个前缀并在其中添加括号,加号,乘号使得这个前缀能够构成数字n,问你这个最小的前缀是多长。

emmm,机房除我都是群神仙 QAQ。。。本来看了看数据5000,然后觉得字符串的要取的长度可能达到几千。。。然后就有点崩,最后大佬们说只需要十几就OK了。。。学到了…犹豫就会败北!要冲一发,莽一发,要敢于尝试,可以先取个长度在long long承受范围内的也就差不多15左右,然后开始打表。但蒟蒻的我连打表都不会QAQ

这玩意儿究竟要怎么打表呢?我们考虑到有括号的存在,那么对于两个区间 a [ l m i d ] , a [ m i d + 1 ] [ r ] a[l-mid],a[mid+1][r] 一定可以变成 a [ l ] [ m i d ] a [ m i d + 1 ] [ r ] a[l][mid]*a[mid+1][r] a [ l ] [ m i d ] + a [ m i d + 1 ] [ r ] a[l][mid]+a[mid+1][r] ,我们可以拿一个前缀为3的来做个实验,那么可以得到 1 + 13 , 1 13 , 11 + 3 , 11 3 , ( 1 + 1 ) 3 , ( 1 + 1 ) + 3 , 1 ( 1 3 ) , 1 + ( 1 3 ) , 1 1 + 3... 1+13,1*13,11+3,11*3,(1+1)*3,(1+1)+3,1*(1*3),1+(1*3),1*1+3... ,可以看出来,左边和右边确实可以组成左乘右和左加右的形式。而左边有可能也是由这种变化得来,右边也有可能是由这种变化得来,那么我们应该要想到区间DP了,枚举中点,对左右两边进行状态转移,那么就可以得到在每个长度下左右两边的状态。

理论上是OK的,现在就是想想要怎么写了,这个是个区间DP,那么我们肯定要枚举区间长度,枚举左端点,得出右端点,枚举中点,枚举 a [ l ] [ m i d ] , a [ m i d + 1 ] [ r ] a[l][mid],a[mid+1][r] 的状态,那么emmm,似乎好像也就这些了,至于 a [ l ] [ m i d ] , a [ m i d + 1 ] [ r ] a[l][mid],a[mid+1][r] 的状态,我们可以用个 v e c t o r vector 来保存,那么就可以得到如下区间DP的代码:

for (int i=2; i<=15; i++) 
	for (int l=1; l+i-1<=15; l++) {
		int r=l+i-1;
		for (int mid=l; mid<=r; mid++) 
			for (auto x:dp[l][mid]) 
				for (auto y:dp[mid+1][r]) {
					if (x+y<=5000 && !vis[l][r][x+y]) {
						dp[l][r].push_back(x+y);
						vis[l][r][x+y]=1;
					}
					if (x*y<=5000 && !vis[l][r][x*y]) {
						dp[l][r].push_back(x*y);
						vis[l][r][x*y]=1;
					}
				}
	}

现在就是要考虑一下初始化了,只需要初始化长度为1的吗?我们看 1 + 13 , 1 13 1+13,1*13 ,很显然, 13 13 这个数字不可能是通过转移过来的,他就是个初始状态!!!所以,我们要将所有区间的的初始状态都记录下来,那么也就可以得到:

for (int i=1; i<=15; i++) {
	for (int j=i; j<=15; j++) {
		ll p=get_num(i,j);
		if (p>5000) continue;
		dp[i][j].push_back(p);
		vis[i][j][p]=1;
	}
}

于是我们就可以愉快地得到一个AC代码了^ _ ^
以下是AC代码:

#include <bits/stdc++.h>
using namespace std;

#define debug1 printf("@!$@$@!\n")
#define debug2(x) printf("%d\n",x )
typedef long long ll;
const int mac=50;

int a[mac],ans[5050];
vector<ll>dp[50][50];
bool vis[50][50][5010];

ll get_num(int l,int r)
{
    ll ans=0;
    for (int i=l; i<=r; i++)
        ans=ans*10+a[i];
    return ans; 
}

int main(int argc, char const *argv[])
{
    string s="1145141919";
    s+=s;
    int len=s.length();
    for (int i=0; i<len; i++)
        a[i+1]=s[i]-'0';
    for (int i=1; i<=15; i++){
        for (int j=i; j<=15; j++){
            ll p=get_num(i,j);
            if (p>5000) continue;
            dp[i][j].push_back(p);
            vis[i][j][p]=1;
        }
    }
    for (int i=2; i<=15; i++){
        for (int l=1; l+i-1<=15; l++){
            int r=l+i-1;
            for (int mid=l; mid<=r; mid++){
                for (auto x:dp[l][mid]){
                    for (auto y:dp[mid+1][r]){
                        if (x+y<=5000 && !vis[l][r][x+y]){
                            dp[l][r].push_back(x+y);
                            vis[l][r][x+y]=1;
                        }
                        if (x*y<=5000 && !vis[l][r][x*y]){
                            dp[l][r].push_back(x*y);
                            vis[l][r][x*y]=1;
                        }
                    }
                }
            }
        }
    }
    for (int i=1; i<=15; i++){
        for (auto x:dp[1][i]){
            //debug2(x);
            if (x<=5000 && !ans[x]) ans[x]=i;
        }
    }//debug1;
    int t;
    scanf ("%d",&t);
    while (t--){
        int n;
        scanf ("%d",&n);
        if (ans[n]) printf("%d\n",ans[n]);
        else printf("-1\n");
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_43906000/article/details/107890254