题解:B4093 [CSP-X2021 山东] 发送快递
guoshengyu1231 · · 题解
题目传送门
初步思考
看了看题面,可以知道这应该是一道状态压缩 dp 的模版题,先不管题目中的限制条件,反正这一定是一道状态压缩 dp 的题目。不妨先假设
具体步骤
确定状态
用一个结构体存储数组
具体的,定义
状态转移
设函数
方程推导
想象一下,我们要在原来的状态中新添加一本书,那我们肯定是要把它放进最空的包裹里,如果连最空的包裹都放不下,那就只能新建一个包裹来放了,所以我们可以计算出新状态,公式如下:
其中
状态转移方程
示例
样例输入:
3 10
4 8 2
此时的
| 状态S | 最优答案 |
|---|---|
| 0 0 1(1) | 1.4 |
| 0 1 0(2) | 1.8 |
| 0 1 1(3) | 2.4 |
| 1 0 0(4) | 1.2 |
| 1 0 1(5) | 1.6 |
| 1 1 0(6) | 1.10 |
| 1 1 1(7) |
格式说明:最优答案用
现在我们已经知道了状态
到目前为止,主要问题其实已经解决了。但还剩下一个问题,那就是
代码
其实我讲的应该算清晰了的,你们可以先自己试试,不懂的再看代码。
#include<bits/stdc++.h>
using namespace std;
int n1,m,a1[25],s,num,n,a[25],fnum;
int f[25];
int find(int x)
{
if(x==f[x]) return x;
return f[x]=find(f[x]);
}
bool cmp(int x1,int y1,int x2,int y2)
{
if(x1==y1) return x2>y2;
return x1>y1;
}
struct{
int x,t;
}dp[1<<23];
int main()
{
cin>>n1>>m;
for(int i=1;i<=n1;i++) f[i]=i;
for(int i=1;i<=n1;i++) cin>>a1[i];
cin>>s;
while(s--)
{
cin>>fnum;
if(cin.get()=='\n') continue;//若输入的是换行,跳过。
while(cin>>num)
{
f[find(num)]=find(fnum);//并查集。
if(cin.get()=='\n') break;//同上。
}
}
for(int i=1;i<=n1;i++)
if(f[i]!=i)
{
a1[f[i]]+=a1[i];
a1[i]=0;
}
for(int i=1;i<=n1;i++)
if(a1[i]) a[++n]=a1[i];
/* 并查集部分还是很好理解的,这里不再过多赘述。*/
/*----------------------DP----------------------*/
for(int i=1;i<=n;i++)
{
dp[(1<<i-1)].x=1;
dp[(1<<i-1)].t=a[i];
}
//初始化,应该不用多讲了吧。
for(int s=1;s<(1<<n);s++)
{
if(!(s&s-1)) continue;
dp[s].x=INT_MAX;//一定要赋初值!
for(int i=1;i<=n;i++)
if((s>>i-1)&1)
{
int x=dp[s^(1<<i-1)].x;
int t=dp[s^(1<<i-1)].t;
if(t+a[i]>m)
{
x++;
t=a[i];
}
else t+=a[i];
if(cmp(dp[s].x,x,dp[s].t,t))
{
dp[s].x=x;
dp[s].t=t;
}
}
}
cout<<dp[(1<<n)-1].x;
return 0;
}