1. 程式人生 > 實用技巧 >2020杭電HDU-6831多校第六場Fragrant numbers(區間DP打表)

2020杭電HDU-6831多校第六場Fragrant numbers(區間DP打表)

題目連結:http://acm.hdu.edu.cn/showproblem.php?pid=6831
CSDN食用連結:https://blog.csdn.net/qq_43906000/article/details/107890254

Problem Description
Many people love numbers, and some have a penchant for specific numbers. Nowadays in popular culture, \(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"\) infinitely stitched together as "\(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≤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\),它可以無限迴圈下去,現在你要選擇一個字首並在其中新增括號,加號,乘號使得這個字首能夠構成數字n,問你這個最小的字首是多長。

emmm,機房除我都是群神仙 QAQ。。。本來看了看資料5000,然後覺得字串的要取的長度可能達到幾千。。。然後就有點崩,最後大佬們說只需要十幾就OK了。。。學到了...猶豫就會敗北!要衝一發,莽一發,要敢於嘗試,可以先取個長度在long long承受範圍內的也就差不多15左右,然後開始打表。但蒟蒻的我連打表都不會QAQ

這玩意兒究竟要怎麼打表呢?我們考慮到有括號的存在,那麼對於兩個區間\(a[l-mid],a[mid+1][r]\)一定可以變成\(a[l][mid]*a[mid+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...\),可以看出來,左邊和右邊確實可以組成左乘右和左加右的形式。而左邊有可能也是由這種變化得來,右邊也有可能是由這種變化得來,那麼我們應該要想到區間DP了,列舉中點,對左右兩邊進行狀態轉移,那麼就可以得到在每個長度下左右兩邊的狀態。

理論上是OK的,現在就是想想要怎麼寫了,這個是個區間DP,那麼我們肯定要列舉區間長度,列舉左端點,得出右端點,列舉中點,列舉\(a[l][mid],a[mid+1][r]\)的狀態,那麼emmm,似乎好像也就這些了,至於\(a[l][mid],a[mid+1][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\),很顯然,\(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;
}