概述
欢迎访问 My Luogu Space。
【题目大意】
有一些正整数,保证这些正整数的和不超过 50000 50000 50000,现在你需要任意取出一些数,使得这些数的和只由 4 4 4 和 7 7 7 组成。如果无法组成则输出“-1”。
【题解】
二进制拆位优化dp
一开始有一个很朴素的dp:
d
p
[
j
]
=
m
i
n
(
d
p
[
j
]
,
d
p
[
j
−
s
[
i
]
+
1
)
dp[j] = min(dp[j],dp[j-s[i]+1)
dp[j]=min(dp[j],dp[j−s[i]+1),
i
i
i 枚举的是这些正整数,
j
j
j 枚举的是从
n
n
n 到
n
−
s
[
i
]
n-s[i]
n−s[i] 内的数。
dp里面的值存的是要取到这个数最少需要取几次。
不过这样的效率会有些不够,我们考虑怎么优化。
我们发现本题给出的数据如果比较大,则会有较多的数字重复,而重复的数字我们没必要一个一个拿进去dp。
假设现在某一个数字重复的次数为 k k k,我们将 k k k 分成 1 , 2 , 4 , 8...... 1,2,4,8...... 1,2,4,8......,最后一组为剩下的数。我们将每一组看成一个新的数字,而这些数字能够组成原本 k k k 个数字能组成的所有的数。我们只需要在更新答案的时候让次数加这些数字的值就可以了。
这样数字的个数就被降了下来,效率得到提高。
【代码】
// output format !!!
// long long !!!
#include <bits/stdc++.h>
typedef long long LL;
using namespace std;
const int MAXN = 50000+10;
const int INF = 0x3f3f3f3f;
int n, m, ans=INF;
int fa[MAXN], num[MAXN], siz[MAXN], dp[MAXN];
vector<int> S;
int G(int x){
return fa[x]==x ? x : fa[x]=G(fa[x]);
}
int can(int x){
while(x){
int y = x%10;
if(y!=4 && y!=7) return 0;
x /= 10;
}
return 1;
}
int main(){
scanf("%d%d", &n, &m);
for(int i=1; i<=n; ++i) fa[i]=i, siz[i]=1;
while(m--){
int a, b; scanf("%d%d", &a, &b);
int x=G(a), y=G(b);
if(x == y) continue;
fa[y] = x, siz[x] += siz[y];
}
for(int i=1; i<=n; ++i)
if(G(i) == i){
if(!num[siz[i]]) S.push_back(siz[i]);
++num[siz[i]];
}
memset(dp, INF, sizeof dp);
dp[0] = 0;
for(auto i=S.begin(); i!=S.end(); ++i){
for(int k=1; k<=num[*i]; num[*i]-=k, k<<=1)
for(int j=n; j>=k*(*i); --j)
dp[j] = min(dp[j], dp[j-k*(*i)]+k);
for(int j=n; j>=num[*i]*(*i); --j)
dp[j] = min(dp[j], dp[j-num[*i]*(*i)]+num[*i]);
}
for(int i=1; i<=n; ++i){
if(dp[i] >= ans) continue;
if(can(i)) ans = dp[i];
}
printf("%d", ans==INF?-1:ans-1);
return 0;
}
最后
以上就是稳重音响为你收集整理的题解 DTOJ #1071. 国王小C kingdom的全部内容,希望文章能够帮你解决题解 DTOJ #1071. 国王小C kingdom所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复