2026hdu暑假7
1002B
通过:797/2344
主要考察状态压缩dp
一个串有 4个状态,问最短的子序列,包含原有的串的所有状态,不包含没有的状态。
有的状态要,有的不要,并求出符合的子序列,容易想到状态压缩dp。算下复杂度是可以的。
设 \(f_{i,j,k}\) 表示枚举到第 \(i\) 位时,包含状态为 \(j\) ,并且以 \(k\) 结尾。时间复杂度是 \(O(2^5 \cdot \sum{|s|})\) 。
空间过大,可以滚动 \(i\) 。
Code:
#include<bits/stdc++.h>
using namespace std;
const int N=3e6+5;
const int inf=0x3f3f3f3f;
int T;
int n;
char c[N];
int res0;
int f[2][16][2];
int main(){
scanf("%d",&T);
while(T--){
scanf("%s",c+1);
n=strlen(c+1);
res0=0;
for(int i=2;i<=n;i++){
if(c[i-1]=='0'&&c[i]=='0'){
res0|=1;
}
else if(c[i-1]=='0'&&c[i]=='1'){
res0|=(1<<1);
}
else if(c[i-1]=='1'&&c[i]=='0'){
res0|=(1<<2);
}
else{
res0|=(1<<3);
}
}
for(int i=0;i<=15;i++){
f[0][i][0]=f[0][i][1]=f[1][i][0]=f[1][i][1]=inf;
}
for(int i=1;i<=n;i++){
for(int j=0;j<=15;j++){ //不选
f[(i&1)][j][0]=f[(i&1)^1][j][0];
f[(i&1)][j][1]=f[(i&1)^1][j][1];
}
for(int j=0;j<=15;j++){ //选
if(c[i]=='0'){
f[(i&1)][j|(1)][0]=min(f[(i&1)][j|(1)][0],f[(i&1)^1][j][0]+1);
f[(i&1)][j|(1<<2)][0]=min(f[(i&1)][j|(1<<2)][0],f[(i&1)^1][j][1]+1);
}
else{
f[(i&1)][j|(1<<1)][1]=min(f[(i&1)][j|(1<<1)][1],f[(i&1)^1][j][0]+1);
f[(i&1)][j|(1<<3)][1]=min(f[(i&1)][j|(1<<3)][1],f[(i&1)^1][j][1]+1);
}
}
f[(i&1)][0][c[i]-'0']=1;
}
printf("%d\n",min(f[n&1][res0][0],f[n&1][res0][1]));
}
return 0;
}
1009I
通过:333/957
主要考察dp状态的设计,dp转移的优化。
数据范围给了关键提示,\(\sum nm^2 \le 1.25 \times 10^8\) 。设说明算法是 \(O(nm^2)\) 的。这个题的操作看上去没什么规律,且复杂度放的宽,应该没什么优秀策略,直接设计dp。求最大撞墙次数,就先设计 \(f_{i,j,k}\) ,表示进行第 \(i\) 轮操作,到位置 \(j\) 时,得到 \(k\) 个草莓时最多碰了 \(f\) 次。算算状态数,已经爆了。一个常见的优化方法是,将 \(k\) 与 \(f\) 存储的内容进行交换。因为最多撞墙 \(m\) 次,状态数就来到了 \(nm^2\) 。可以拿它来枚举。统计答案时,按照 \(k\) 从大到小枚举,直到枚举到草莓数量能够接受,此时的 \(k\) 就是答案。
状态数大,此时要求转移效率高,必须平均 \(O(1)\) 。假设 \(w\) 是向左右走的范围,\(c\) 是草莓数,\(lim\) 是草莓数量限制,分类讨论:
- \(2 \le j \le n-1\) 时,\(\displaystyle f_{i,j,k}=\min_{p=l}^r{f_{i-1,p,k}} \ ,其中 \ l=\max(1,j-w_i),r=\min(n,j+w_i)\) (小于 \(w_i\))和 \(\displaystyle f_{i,j,k}=\min(f_{i-1,j-w_i,k},f_{i-1,j+w_i,k})+c_i\) (等于 \(w_i\))。在给 \(l,r\) 的区间求 \(\min\) 时,不能暴力枚举。发现是一个不断向右移动的区间,用单调队列优化。
- \(j=1 \ 或 \ j=n\) 时,\(\displaystyle f_{i,1,k}=\min_{p=1}^{w_i}{f_{i-1,p,k-1}}+c_i\) (撞墙),\(\displaystyle f_{i,1,k}=\min_{p=1}^{1+w_i}{f_{i-1,p,k}}\) (没撞墙但小于 \(w_i\)),\(f_{i,1,k}=\min f_{i,1+w_i,k}+c_i\) (没撞墙但等于 \(w_i\))。
空间大了。可以对 \(i\) 滚动。时间复杂度 \(O(nm^2)\) ,比较考验代码能力。
本题只有 2000ms,轻微卡常,不要使用 deque,最好手写。
Code:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=505;
const ll inf=0x3f3f3f3f3f3f3f3f;
ll read(){
ll f=1,x=0;
char c=getchar();
while(c<'0'||c>'9'){
if(c=='-')f=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
x=(x<<1)+(x<<3)+(c^48);
c=getchar();
}
return f*x;
}
int T;
int n,m,s;
ll w[N],c[N];
ll lim[N];
ll f[2][N][N]; //第i轮,在j,撞墙k次,最小f个草莓
int q[N],hd,tl;
int ans[N];
void init(int id){
for(int i=1;i<=n;i++){
for(int j=0;j<=m;j++){
f[id][i][j]=inf;
}
}
}
int main(){
T=read();
while(T--){
n=read(),m=read(),s=read();
for(int i=1;i<=m;i++){
w[i]=read(),c[i]=read();
}
for(int i=1;i<=n;i++){
lim[i]=read();
}
init(0),init(1);
f[0][s][0]=0;
for(int i=1;i<=m;i++){ //第i轮
int id=i&1;
init(id);
for(int k=0;k<=m;k++){ //撞墙k次
hd=0,tl=1;
int l=max(1LL,2-(w[i]-1)+1),r=min(1LL*n,2+(w[i]-1)-1);
for(int j=l;j<=r;j++){ //准备单调队列
while(hd>=tl){
int u=q[hd];
if(f[id^1][j][k]<=f[id^1][u][k])hd--;
else break;
}
q[++hd]=j;
}
for(int j=2;j<=n-1;j++){ //一定没撞墙
l=max(1LL,j-(w[i]-1)),r=min(1LL*n,j+(w[i]-1)); //没到wi
while(hd>=tl){
int u=q[hd];
if(f[id^1][r][k]<=f[id^1][u][k])hd--;
else break;
}
while(hd>=tl){
int u=q[tl];
if(u<l)tl++;
else break;
}
q[++hd]=r;
int u=q[tl];
f[id][j][k]=min(f[id][j][k],f[id^1][u][k]);
int now=j-w[i]; //到wi
if(now>=1){
f[id][j][k]=min(f[id][j][k],f[id^1][now][k]+c[i]);
}
now=j+w[i];
if(now<=n){
f[id][j][k]=min(f[id][j][k],f[id^1][now][k]+c[i]);
}
}
//以上讨论2<=j<=n-1 下面讨论1,n
//没到wi
for(int j=1;j<=w[i];j++){
if(k!=0)f[id][1][k]=min(f[id][1][k],f[id^1][j][k-1]+c[i]); //撞到1
f[id][1][k]=min(f[id][1][k],f[id^1][j][k]); //到1没撞
}
for(int j=n-w[i]+1;j<=n;j++){
if(k!=0)f[id][n][k]=min(f[id][n][k],f[id^1][j][k-1]+c[i]); //撞到n
f[id][n][k]=min(f[id][n][k],f[id^1][j][k]); //到n没撞
}
//到wi
if(1+w[i]<=n)f[id][1][k]=min(f[id][1][k],f[id^1][1+w[i]][k]+c[i]);
if(n-w[i]>=1)f[id][n][k]=min(f[id][n][k],f[id^1][n-w[i]][k]+c[i]);
}
}
for(int i=1;i<=n;i++)ans[i]=-1;
for(int i=1;i<=n;i++){
for(int j=m;j>=0;j--){
if(f[m&1][i][j]<=lim[i]){
ans[i]=j;
break;
}
}
}
for(int i=1;i<=n;i++)printf("%d ",ans[i]);
puts("");
}
return 0;
}

浙公网安备 33010602011771号