AcWing DP题目杂选
529.宝藏
- 状压DP
- 根据层数来一层一层往下推,每次同时更新接下来再走一步可选的若干条边;
- 我们可以事先处理出一个数组表示当前状态再走一步可以达到的点最多的状态,后面计算时直接取 \(&\) 即可;
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=15;
const int X=0x3f3f3f3f;
int n,m;
int road[N][N];
int f[N][(1<<13)+10];
int ex[(1<<13)+10];
int main(){
scanf("%d%d",&n,&m);
memset(road,0x3f,sizeof(road));
memset(f,0x3f,sizeof(f));
for(int i=0;i<n;++i)
road[i][i]=0;
int a,b,c;
for(int i=0;i<m;++i){
scanf("%d%d%d",&a,&b,&c);
--a,--b;//方便计算,从0开始
road[a][b]=road[b][a]=min(road[b][a],c);
}
for(int i=0;i<(1<<n);++i){
for(int j=0;j<n;++j)
if((i>>j)&1){
for(int k=0;k<n;++k)
if(road[j][k]<X){
ex[i]|=(1<<k);
}
}
// printf("%d %d\n",i,ex[i]);
}
for(int i=0;i<n;++i)
f[0][1<<i]=0;
int ans=INT_MAX;
for(int i=0;i<(1<<n);++i){
for(int j=(i-1)&i;j;j=(j-1)&i){
if((ex[j]&i)==i){
int nw=i^j,cost=0;
for(int k=0;k<n;++k){
if(!((nw>>k)&1)) continue;
int t=X;
for(int x=0;x<n;++x)
if((j>>x)&1) t=min(t,road[x][k]);
cost+=t;
}
// printf("%d %d\n",i,nw);
// printf("%d %d\n",i,cost);
for(int k=1;k<n;++k)
f[k][i]=min(f[k][i],f[k-1][j]+cost*k);
}
}
}
for(int i=0;i<n;++i)
ans=min(ans,f[i][(1<<n)-1]);
// for(int i=0;i<n;i++)
// printf("%d %d\n",i,f[i][(1<<n)-1]);
printf("%d",ans);
return 0;
}
283.多边形
- 一道玄妙的区间DP;
- 本题的关键之处在于删边时产生一条链,我们可以使用旋转来进行简化计算;
此处我选择把删边后的第一个点转到起点位置,方便计算; - 最重要的一点在于:
对于最大值的计算,由于题目中有负数的可能性,所以我们既要考虑最大的两个正数相乘,也要考虑最小的两个负数相乘(负负得正),因此在dp时最好同时记录最大值和最小值;
没有必要考虑最大值和最小值的正负性,算就完了。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
char ch[55];
int num[55];
int f[55][55][55][2];
int n;
int g(int x,int y,char z){
// printf("%d %d ",x,y);
// cout<<z<<endl;
return (z=='t')?x+y:x*y;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;++i){
cin>>ch[i];
scanf("%d",&num[i]);
}
memset(f,-127,sizeof(f));
for(int i=0;i<55;++i)
for(int j=0;j<55;++j)
for(int k=0;k<55;++k)
f[i][j][k][1]=0x3f3f3f3f;
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j){
int x=(j-i+n)%n+1;
f[i][x][x][0]=f[i][x][x][1]=num[j];
// printf("%d %d %d\n",i,j,x);
}
for(int i=1;i<=n;++i){
// printf("Now we delete line %d\n",i);
for(int l=1;l<n;++l){
for(int s=1;s+l<=n;++s){
int e=s+l;
// printf("%d %d %d %d\n",i,l,s,e);
for(int k=s;k<s+l;++k){
// if(i==45)
// printf("%d %d %d\n",s,e,k);
f[i][s][e][0]=max(f[i][s][e][0],g(f[i][s][k][0],f[i][k+1][e][0],ch[(i+k-1)%n+1]));
f[i][s][e][0]=max(f[i][s][e][0],g(f[i][s][k][1],f[i][k+1][e][1],ch[(i+k-1)%n+1]));
f[i][s][e][1]=min(f[i][s][e][1],g(f[i][s][k][1],f[i][k+1][e][0],ch[(i+k-1)%n+1]));
f[i][s][e][1]=min(f[i][s][e][1],g(f[i][s][k][0],f[i][k+1][e][1],ch[(i+k-1)%n+1]));
// if(i==45)
// printf("%d\n",f[i][s][e][0]);
}
}
}
}
int ans=-0x3f3f3f3f;
vector<int>ind;
for(int i=1;i<=n;++i){
if(f[i][1][n][0]>ans){
ind.clear();
ind.push_back(i);
ans=f[i][1][n][0];
}
else if(f[i][1][n][0]==ans)
ind.push_back(i);
}
printf("%d\n",ans);
for(int i=0;i<ind.size();++i)
printf("%d ",ind[i]);
return 0;
}
284.金字塔
- 一道奇怪的区间DP;
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int mod=1e9;
string s;
long long f[305][305];
int dfs(int l,int r){
// printf("%d %d\n",l,r);
if(l>r) return 0;
if(l==r) return 1;
if(f[l][r]>-1) return f[l][r];
f[l][r]=0;
if(s[l]==s[r])
for(int i=l+2;i<=r;++i)
(f[l][r]+=1ll*dfs(l+1,i-1)*dfs(i,r))%=mod;
return f[l][r];
}
int main(){
cin>>s;
memset(f,-1,sizeof(f));
printf("%d",dfs(0,s.size()-1));
return 0;
}
288.休息时间
-
环形DP;
-
将所有情况分为两大类:一种是第一个小时的恢复值有效(即存在跨夜的情况),一种是无效;
然后我们将这两种情况分开分别强制执行,统计两次DP的结果即可;
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=3840;
int n,b;
int u[N];
long long ans=0;
long long f[2][N][2];
int main(){
scanf("%d%d",&n,&b);
for(int i=1;i<=n;++i)
scanf("%d",&u[i]);
memset(f,-127,sizeof(f));
f[1][1][1]=f[1][0][0]=0;
for(int i=2,s=0;i<=n;++i,s^=1){
for(int j=min(i,b);j>-1;--j){
f[s][j][0]=max(f[s^1][j][1],f[s^1][j][0]);
if(j) f[s][j][1]=max(f[s^1][j-1][0],f[s^1][j-1][1]+u[i]);
// printf("%d %d %d\n",i,j,f[i][j][1]);
}
}
ans=max(ans,f[n%2][b][0]);
memset(f,-127,sizeof(f));
f[1][1][1]=u[1];
f[1][0][0]=0;
for(int i=2,s=0;i<=n;++i,s^=1){
for(int j=min(i,b);j>-1;--j){
f[s][j][0]=max(f[s^1][j][1],f[s^1][j][0]);
// f[s][j][1]=max(f[s][j][1],f[s^1][j-1][0]);
if(j) f[s][j][1]=max(f[s^1][j-1][0],f[s^1][j-1][1]+u[i]);
// printf("%d %d %d\n",i,j,f[i%2][j][1]);
}
}
ans=max(ans,f[n%2][b][1]);
printf("%lld",ans);
return 0;
}