专项训练 状态压缩dp
1.[USACO13NOV] No Change G
因为硬币的数据范围极小,考虑对选择硬币的状态进行状压。状态
接下来枚举所有的状态,对于每一个状态枚举当前选择的货币
其中
最后统计答案,当
最后取 max 即可。
代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=1e5+10;
int k,n,a[maxn],sum[maxn],dp[1<<(20)];
int ans,cnt,fl=0;
int find(int lim){//表示价值为lim的货币可以从1连续购买到的最大商品的 下标!!
int l=1,r=n,pos=0;
while(l<=r){
int mid=(l+r)>>1;
if(sum[mid]<=lim) l=mid+1,pos=mid;
else r=mid-1;
}
return pos;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>k>>n;
for(int i=1;i<=k;i++){
cin>>a[i];
}
for(int i=1;i<=n;i++){
cin>>sum[i];
sum[i]+=sum[i-1];
}
for(int p=0;p<(1<<k);p++){//枚举所有选硬币的状态
for(int i=1;i<=k;i++){
if(!(p&(1<<(i-1)))) continue;
int pos=find(sum[dp[p^(1<<(i-1))]]+a[i]);
dp[p]=max(dp[p],pos);
}
}
for(int p=0;p<(1<<k);p++){
if(dp[p]==n){
fl=1;
int cnt=0;
for(int i=1;i<=k;i++){
if(!(p&(1<<(i-1)))) cnt+=a[i];
}
ans=max(ans,cnt);
}
}
if(fl==0) cout<<-1<<endl;
else cout<<ans<<endl;
}
2.[HAOI2007] 修筑绿化带
3.CF620E New Year Tree
dfs序+状压。
对于此类修改子树的问题,很容易可以想到将它整理成线性结构,使用数据结构维护,做区间修改操作。
对原树跑一遍dfs序,对dfs序建立线段树,把颜色压成二进制存到线段树中,(因为c的范围为60,所以需要开long long !)然后做区间修改区间查询操作即可。
代码
//感觉是dfs+线段树的典题啊,怎么用状压??
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=4e5+10;
int n,a[maxn],m;
struct edge{
int to,nxt;
}e[2*maxn];
int head[maxn],edgenum;
void add_edge(int u,int v){
e[++edgenum].nxt=head[u];
e[edgenum].to=v;
head[u]=edgenum;
}
int in[maxn],out[maxn],tim,pos[maxn];
void dfs(int x,int fa){
tim++;
in[x]=tim;
pos[tim]=x;
for(int i=head[x];i;i=e[i].nxt){
int v=e[i].to;
if(v==fa) continue;
dfs(v,x);
}
out[x]=tim;
}
struct seg_ment_tree{
int l,r,ans,lazy;
}tr[4*maxn];
#define lid id*2
#define rid id*2+1
void pushup(int id){
tr[id].ans=tr[lid].ans|tr[rid].ans;
}
void pushdown(int id){
if(tr[id].lazy!=0){
tr[lid].lazy=tr[id].lazy;
tr[rid].lazy=tr[id].lazy;
tr[lid].ans=tr[id].lazy;
tr[rid].ans=tr[id].lazy;
tr[id].lazy=0;
}
}
void build(int id,int l,int r){
tr[id].l=l,tr[id].r=r;
if(l==r){
tr[id].ans=1ll<<(a[pos[l]]);
return;
}
int mid=(l+r)/2;
build(lid,l,mid);
build(rid,mid+1,r);
pushup(id);
}
void update(int id,int l,int r,int x){
if(tr[id].l==l && tr[id].r==r){
tr[id].ans=1ll<<x;
tr[id].lazy=1ll<<x;
return;
}
pushdown(id);
int mid=(tr[id].l+tr[id].r)/2;
if(r<=mid) update(lid,l,r,x);
else if(l>mid) update(rid,l,r,x);
else update(lid,l,mid,x),update(rid,mid+1,r,x);
pushup(id);
}
int query(int id,int l,int r){
if(tr[id].l==l && tr[id].r==r){
return tr[id].ans;
}
pushdown(id);
int mid=(tr[id].l+tr[id].r)/2;
if(r<=mid) return query(lid,l,r);
else if(l>mid) return query(rid,l,r);
else return query(lid,l,mid)|query(rid,mid+1,r);
}
int lowbit(int x){
return x&(-x);
}
int getsum(int x){
int ans=0;
for(int i=x;i>0;i-=lowbit(i)) ans++;
return ans;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=1,u,v;i<n;i++){
cin>>u>>v;
add_edge(u,v);
add_edge(v,u);
}
dfs(1,0);
build(1,1,n);
for(int i=1,op,u,c;i<=m;i++){
cin>>op;
if(op==1){
cin>>u>>c;
update(1,in[u],out[u],c);
}
else{
cin>>u;
int num=query(1,in[u],out[u]);
// cout<<"1!!!!!!!!"<<num<<endl;
cout<<getsum(num)<<endl;
}
}
}
4.[COCI2016-2017#1] Vještica
先考虑简单情况。如果只有两个串,那么
考虑将其扩展到多个字符串,令
对于实现,先记录
代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
int n;
string c;
int pre[maxn][30],sum[30][30],dp[maxn];
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n;
memset(dp,0x3f,sizeof(dp));
memset(pre,0x3f,sizeof(pre));
for(int i=1;i<=n;i++){
cin>>c;
c=" "+c;
for(int j=c.size()-1;j;j--)
sum[i][c[j]-'a'+1]++;//记录第i种串每个字母出现的次数
sum[i][0]=c.size()-1;
dp[1<<i-1]=sum[i][0];
}
for(int s=1;s<(1<<n);s++){//枚举每一个状态
for(int i=1;i<=n;i++){
if((s>>i-1)&1){//当前状态考虑第i个串
for(int j=1;j<=26;j++){
pre[s][j]=min(pre[s][j],sum[i][j]);
}
}
}
pre[s][0]=0;
for(int i=1;i<=26;i++) pre[s][0]+=pre[s][i];
for(int p=s&(s-1);p;p=(p-1)&s){
dp[s]=min(dp[s],dp[p]+dp[p^s]-pre[s][0]);
}
}
cout<<dp[(1<<n)-1]+1<<endl;
}
“当蓝色的夜坠落在世界时,没人看见我们手牵着手。”
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律