目录
牛客_DP59数位染色_01背包
描述:
小红拿到了一个正整数 x 。她可以将其中一些数位染成红色。然后她想让所有染红的数位数字之和等于没染色的数位数字之和。
她不知道能不能达成目标。你能告诉她吗?
输入描述:
一个正整数 x ,1≤x≤10^18
输出描述:
如果小红能按要求完成染色,输出"Yes"。否则输出"No"。
题目解析
选出一个最高达18位数的整数nn十进制中的一些位,这些位数字和等于没选的位的数字和,如果可以输出Yes,否则输出No。
首先使用连除法将n的每位数字依次记录在数组中,并在这个过程中求得所有位数之和,那我们要找的就是能否在这个数组中找到诺干个数字加起来等于位数之和的一半。
首先排除和为奇数的情况,无法构成一半。然后我们用一个长度为和一半的dp数组,dp[i]不为0表示和为ii的情况是可以被找到的,为0代表找不到,初始化都为0。我们遍历数组中记录的每位数字sum[i],每次再遍历sum/2到该数字,如果dp[j]可以被相加得到,说明j−sum[i]已经得到了,或者在之前的遍历中得到过,于是就有转移方程dp[j]=dp[j]+dp[j−num[i]],最后检查dp[sum/2]是否为0即可。
C++代码
#include <iostream>
#include <vector>
using namespace std;
int main()
{ // 选和不选,01背包?
string str;
cin >> str;
str = ' ' + str;
int n = str.size() - 1, sum = 0;
for(int i = 1; i <= n; ++i)
{
sum += str[i] - '0';
}
if(sum % 2 == 1) // 是奇数
{
cout << "No" << endl;
return 0;
}
// // 选一些数,使其等于sum / 2;
// sum /= 2;
// vector<int> dp(sum + 1); // 滚动数组优化
// // dp[i][j] 表示,从0选到i,能否凑成总和为sum
// for(int i = 1; i <= n; ++i)
// {
// for(int j = sum; j >= 0; --j)
// {
// // dp[i][j] = dp[i - 1][j]; // 不选
// int val = str[i] - '0';
// if(j >= val)
// dp[j] = max(dp[j], dp[j - val] + val); // 选
// // cout << dp[i][j] << endl;
// }
// }
// if(dp[sum] == sum)
// cout << "Yes" << endl;
// else
// cout << "No" << endl;
// // 选一些数,使其等于sum / 2;
// sum /= 2;
// vector<vector<int>> dp(n + 1, vector<int>(sum + 1));
// // dp[i][j] 表示,从0选到i,总和不超过j的数的总和
// for(int i = 1; i <= n; ++i)
// {
// for(int j = 1; j <= sum; ++j)
// {
// dp[i][j] = dp[i - 1][j]; // 不选
// int val = str[i] - '0';
// if(j >= val)
// dp[i][j] = max(dp[i][j], dp[i - 1][j - val] + val); // 选
// // cout << dp[i][j] << endl;
// }
// }
// if(dp[n][sum] == sum)
// cout << "Yes" << endl;
// else
// cout << "No" << endl;
// 选一些数,使其等于sum / 2; // bool 类型 滚动数组优化
sum /= 2;
vector<bool> dp(sum + 1);
dp[0] = true; // 注意初始化,不选就为true
// dp[i][j] 表示,从0选到i,总和能否凑成j
for(int i = 1; i <= n; ++i)
{
int val = str[i] - '0';
for(int j = sum; j >= val; --j)
{
dp[j] = dp[j] || dp[j - val]; // 选
}
}
if(dp[sum])
cout << "Yes" << endl;
else
cout << "No" << endl;
// // 选一些数,使其等于sum / 2; // bool 类型
// sum /= 2;
// vector<vector<bool>> dp(n + 1, vector<bool>(sum + 1, false));
// dp[0][0] = true; // 注意初始化,不选就为true
// // dp[i][j] 表示,从0选到i,总和能否凑成j
// for(int i = 1; i <= n; ++i)
// {
// for(int j = 1; j <= sum; ++j)
// {
// dp[i][j] = dp[i - 1][j]; // 不选
// int val = str[i] - '0';
// if(j >= val)
// dp[i][j] = dp[i][j] || dp[i - 1][j - val]; // 选
// // cout << dp[i][j] << endl;
// }
// }
// if(dp[n][sum])
// cout << "Yes" << endl;
// else
// cout << "No" << endl;
return 0;
}
Java代码
import java.util.Scanner;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main
{
public static void main(String[] args)
{
Scanner in = new Scanner(System.in);
long x = in.nextLong();
int[] arr = new int[20];
int n = 0, sum = 0;
while(x != 0)
{
int t = (int)(x % 10);
arr[n++] = t;
sum += t;
x /= 10;
}
if(sum % 2 == 1)
{
System.out.println("No");
}
else
{
sum /= 2;
boolean[] dp = new boolean[sum + 1];
dp[0] = true;
for(int i = 0; i < n; i++)
{
for(int j = sum; j >= arr[i]; j--)
{
dp[j] = dp[j] || dp[j - arr[i]];
}
}
if(dp[sum])
System.out.println("Yes");
else
System.out.println("No");
}
}
}