暴力打表+区间dp [ 2020 Multi-University Fragrant numbers]

暴力打表+区间dp 2020 Multi-University Fragrant numbers

题目大意:

有一个字符串 (1145141919) ,它可以重复,无限变长, (114514191911451419191145141919...)

你可以取这个字符串的一个前缀,在这个前缀插入 (() ()) (+) (*) ,形成一个合法的四则运算,问最短的前缀使得四则运算的结果等于 (n)

题解:

因为 (N<=5000) ,那么合理猜测,前面20个前缀可以组成这5000个数。

所以先打个表,判断一个前面20个数可以构成多少个5000以内的数。

直接暴力dfs不太好写,考虑区间dp来合并。

  • 首先预处理直接黏在一起的部分
  • 然后区间求可能的所有值,用vector存下来。

最后发现11个就可以了。。。

#include <bits/stdc++.h>
#define debug(x) cout<<"debug:"<<#x<<" = "<<x<<endl;
using namespace std;
typedef long long ll;
const int maxn = 5e3+10;
vector<int>num[110][110];
bool dp[110][110][maxn];
int vis[maxn];
int a[] = {0,1,1,4,5,1,4,1,9,1,9,1,1,4,5,1,4,1,9,1,9};
int main() {
    int n = 11;
    for (int len = 1; len <= 5; len++) {
        for (int i = 1; i + len - 1 <= n; i++) {
            int j = i + len - 1, res = 0;
            for (int x = i; x <= j; x++) res = res * 10 + a[x];
            if (res > 5000||dp[i][j][res]) continue;
            dp[i][j][res] = true;
            num[i][j].push_back(res);
        }
    }
    for (int len = 1; len <= n; len++) {
        for (int i = 1; i + len - 1 <= n; i++) {
            int j = i + len - 1;
            for (int k = i; k < j; k++) {
            	int len1 = num[i][k].size();
                for (int x = 0; x < len1; x++) {
                	int len2 = num[k + 1][j].size();
                    for (int y = 0; y < len2; y++) {
                        int u = num[i][k][x], v = num[k + 1][j][y];
                        ll res = 1ll * u * v;
                        if (res <= 5000&&!dp[i][j][res]) {
                            dp[i][j][res] = true;
                            num[i][j].push_back(res);
                        }
                        res = u + v;
                        if (res <= 5000&&!dp[i][j][res]) {
                            dp[i][j][res] = true;
                            num[i][j].push_back(res);
                        }
                    }
                }
            }
        }
    }
    int tot = 0;
    for(int i=1;i<=5000;i++) {
        for (int j = 1; j <= n; j++) {
            if (dp[1][j][i] && vis[i] == 0) {
                tot++,vis[i] = j;
                break;
            }
        }
    }
//    debug(tot);
	int T;
	scanf("%d",&T);
	while(T--){
		int m;
		scanf("%d",&m);
		printf("%d
",vis[m]?vis[m]:-1);
	}
}
原文地址:https://www.cnblogs.com/EchoZQN/p/13448838.html