CCF- CSP 202209-2 何以包邮? 两种方法 dfs+离散化 满分题解
题目链接:202209-2 何以包邮?
思路1(离散化):
n
最大为30,a
最大为104,所以最大价格为3e5
- 将所有组合的价格映射到
f
上,从x
开始向大进行查找,直到找到第一个大于等于x
的价格(存在此组合) - 技巧在于求各种组合的价格,也是采用离散化的思想,将每一个价格和组合映射到
f
上,每次从M
开始遍历整个f
,找到加入当前price
后,此price
和之前的组合形成的新组合所对应的值
代码如下:
#include<iostream>
using namespace std;
const int N = 50, M = 3e5 + 10;//M的范围得大
int n,x;
int price[N];//价格
int f[M];//存储离散化后的点
int main()
{
cin>>n>>x;
for(int i=1;i<=n;i++)
{
cin>>price[i];
}
//得到各种组合后的价格
f[0]=1;//初始化
for(int i=1;i<=n;i++)
{
for(int j=M;j>=price[i];j--)
{
if(f[j-price[i]])
{
f[j]=1;
}
}
}
int out = x;//起始位置
while(1)
{
if(f[out])break;//找到第一个大于等于x的价格
else
{
out++;
}
}
cout<<out<<endl;
return 0;
}
思路2(dfs不剪枝70分):
- 对于每本书,都有选和不选两种方案,可以对应一棵完全二叉树
- 遍历所有书的两种方案,找到最小值,采用
dfs
算法
70分代码如下:
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 50;
int n,x;
int price[N];
int ans = 1e9;
void dfs(int k,int sum)
{
if(sum>=x)
{
ans = min(sum,ans);//更新最小值
}
if(k>n)
{
return;
}
dfs(k+1, sum+price[k]);//选择这本书
dfs(k+1,sum);//不选择这本书
}
int main()
{
cin>>n>>x;
for(int i=1;i<=n;i++)
{
cin>>price[i];
}
dfs(1,0);//从第一本书开始
cout<<ans<<endl;
}
思路三(dfs+剪枝100分):
- 在思路二的基础上,对二叉树进行剪枝
- 剪枝思路:当前节点
sum
加上剩余节点的所有值之和已经小于x
了,进行剪枝 - 求剩余节点的所有值,可以采用前缀和的思想
代码如下:
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 50;
int n,x;
int price[N];//价格
int sum_price[N];//前缀和
int ans = 1e9;
void dfs(int k,int sum)
{
if(sum+sum_price[n]-sum_price[k-1]<x)//sum_price[n]-sum_price[k-1]为剩余节点的所有值的和
{
return;
}
if(sum>=x)
{
ans = min(sum,ans);//更新最小值
}
if(k>n)
{
return;
}
dfs(k+1,sum+price[k]);//选择这本书
dfs(k+1,sum);//不选择这本书
}
int main()
{
cin>>n>>x;
for(int i=1;i<=n;i++)
{
cin>>price[i];
sum_price[i]=sum_price[i-1]+price[i];//前缀和
}
dfs(1,0);//从第一本书开始
cout<<ans<<endl;
}