呃呃 今天cf提前了一个小时没看到
AcWing 91. 最短Hamilton路径
因为这个图是个完全图 所以很轻易推出
状态表示:f_i_j 表示为 到i这个点 并且状态为 j 的min
状态计算:f[i][j]=min(f[i][j],f[i-(1<<j)][k]+a[k][j]);枚举每一个合法的k
#include <bits/stdc++.h>
using namespace std;
const int N = 22;
const int M = 1<<20;
const int mod = 1e9+7;
#define int long long
#define endl '\n'
#define Endl '\n'
#define inf 0x3f3f3f3f3f3f3f3f
#define fast ios::sync_with_stdio(false);cin.tie(nullptr);
int max(int x,int y){return x>y?x:y;}
int min(int x,int y){return x<y?x:y;}
int n,a[M][N],f[M][N];
signed main(){
fast
cin>>n;
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
cin>>a[i][j];
memset(f,0x3f3f,sizeof f);
f[1][0]=0;
for(int i=1;i<1<<n;i++)
for(int j=0;j<n;j++)
if(i>>j&1)
for(int k=0;k<n;k++)
if((i-(1<<j))>>k&1)
f[i][j]=min(f[i][j],f[i-(1<<j)][k]+a[k][j]);
cout<<f[(1<<n)-1][n-1];
return 0^0;
}
AcWing 285. 没有上司的舞会
树形dp
我们可以只维护fa【】
然后状态表示为f【i】【2】 0表示i没有被选择 1 表示被选择了
状态计算:f[x][0]+=max(f[j][0],f[j][1]); f[x][1]+=f[j][0];
#include <bits/stdc++.h>
using namespace std;
const int N = 6e3+10;
const int M = 1<<20;
const int mod = 1e9+7;
#define int long long
#define endl '\n'
#define Endl '\n'
#define inf 0x3f3f3f3f3f3f3f3f
#define fast ios::sync_with_stdio(false);cin.tie(nullptr);
int max(int x,int y){return x>y?x:y;}
int min(int x,int y){return x<y?x:y;}
int w[N],n,h[N],e[N<<1],ne[N<<1],idx,fa[N],f[N][2];
void add(int a,int b){
e[idx]=b;
ne[idx]=h[a];
h[a]=idx++;
}
void dfs(int x){
f[x][1]=w[x];
for(int i=h[x];~i;i=ne[i]){
int j=e[i];
dfs(j);
f[x][0]+=max(f[j][0],f[j][1]);
f[x][1]+=f[j][0];
}
}
signed main(){
fast
cin>>n;
for(int i=1;i<=n;i++)cin>>w[i];
memset(h,-1,sizeof h);
for(int i=1;i<n;i++){
int a,b;cin>>a>>b;
fa[a]=b;
add(b,a);
}
int root=1;
while(fa[root])root++;
dfs(root);
cout<<max(f[root][0],f[root][1])<<endl;
return 0^0;
}
P1280 尼克的任务
很多种方式可以做啊
比如dp求最少工作时长 记忆化搜索 最短路
但是我选择了代码最短的倒着找
状态表示:f[i]表示i~n的max空闲时长
要是有该点没任务那我们
f[i]=f[i+1]+1;
要是有任务 我们不得不去做任务
for(auto k:v[i]){
f[i]=max(f[i],f[i+k]);
}
完整代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 1e4+10;
const int M = 1<<20;
const int mod = 1e9+7;
#define int long long
#define endl '\n'
#define Endl '\n'
#define inf 0x3f3f3f3f3f3f3f3f
#define fast ios::sync_with_stdio(false);cin.tie(nullptr);
int max(int x,int y){return x>y?x:y;}
int min(int x,int y){return x<y?x:y;}
int n,k,f[N];
vector<int>v[N];
signed main(){
fast
cin>>n>>k;
for(int i=0;i<k;i++){
int a,b;cin>>a>>b;
v[a].push_back(b);
}
for(int i=n;i>=1;i--){
f[i]=-2e9;
if(v[i].empty()){
f[i]=f[i+1]+1;
}else{
for(auto k:v[i]){
f[i]=max(f[i],f[i+k]);
}
}
}
cout<<f[1]<<endl;
return 0^0;
}
P1040 [NOIP2003 提高组] 加分二叉树
这种题一看就是搜索
f[l][r]就是区间lr之间的max
记忆化搜索:
要是这点搜过了 就直接返回 因为我们维护的 就是最优解
我们循环那一步 就是找最优解 最后再return 所以每次我们已经存好的点 一定是最优解
#include <bits/stdc++.h>
using namespace std;
const int N = 35;
const int M = 1<<20;
const int mod = 1e9+7;
#define int long long
#define endl '\n'
#define Endl '\n'
#define inf 0x3f3f3f3f3f3f3f3f
#define fast ios::sync_with_stdio(false);cin.tie(nullptr);
int max(int x,int y){return x>y?x:y;}
int min(int x,int y){return x<y?x:y;}
int n,f[N][N],root[N][N],a[N];
int dfs(int l,int r){
if(l>r)return 1;
if(f[l][r])return f[l][r];
for(int i=l;i<=r;i++){
int t=dfs(l,i-1)*dfs(i+1,r)+a[i];
if(t>f[l][r]){
f[l][r]=t;
root[l][r]=i;
}
}
return f[l][r];
}
void print(int l,int r){
if(l>r)return;
cout<<root[l][r]<<' ';
print(l,root[l][r]-1);
print(root[l][r]+1,r);
}
signed main(){
fast
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
f[i][i]=a[i];
root[i][i]=i;
}
cout<<dfs(1,n)<<endl;
print(1,n);
return 0^0;
}
P4933 大师
最开始想了个暴力的做法去看题解 发现其实都是差不多的
我们将f_i_j 表示为 i 为最后的一个塔 j 为方差
我们直接枚举i前面的每一个点 O(n2)的
状态计算就出来了
f[i][a[i]-a[j]+p]+=f[j][a[i]-a[j]+p]+1;
f[i][a[i]-a[j]+p]%=mod;
最后把ans加起来就完事了
#include <bits/stdc++.h>
using namespace std;
const int N = 4e4+10;
const int M = 1<<20;
const int mod = 998244353;
#define int long long
#define endl '\n'
#define Endl '\n'
#define inf 0x3f3f3f3f3f3f3f3f
#define fast ios::sync_with_stdio(false);cin.tie(nullptr);
int max(int x,int y){return x>y?x:y;}
int min(int x,int y){return x<y?x:y;}
int f[1010][N];
signed main(){
fast
int n;cin>>n;
int a[N],ans=0,p=2e4;
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<=n;i++){
for(int j=1;j<i;j++){
f[i][a[i]-a[j]+p]+=f[j][a[i]-a[j]+p]+1;
f[i][a[i]-a[j]+p]%=mod;
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=4e4;j++){
ans+=f[i][j];
ans%=mod;
}
}
cout<<ans+n<<endl;
return 0^0;
}
P5858 「SWTR-03」Golden Sword
这个题不小心点开了tag 看到了是单调队列优化
不然我可能想不出?
这个题的状态表示显然
因此小 E 必须按照 1 到 n 的顺序依次将这些原料放入炼金锅
但是,炼金锅的容量非常有限,它最多只能容纳 w 个原料。
f_i_j 就表示前i个物品锅里还有j个物品的max
状态计算:f_i_j=max(f[i-1][j+k]+a[i]*j) -1<=k<=s-1
然后套单调队列模板就可以了
#include <bits/stdc++.h>
using namespace std;
const int N = 5e3+10;
const int M = 1<<20;
const int mod = 998244353;
#define int long long
#define endl '\n'
#define Endl '\n'
#define inf 0x3f3f3f3f3f3f3f3f
#define fast ios::sync_with_stdio(false);cin.tie(nullptr);
int max(int x,int y){return x>y?x:y;}
int min(int x,int y){return x<y?x:y;}
int n,w,s,a[N],q[N],f[N][N],pos[N];
signed main(){
fast
cin>>n>>w>>s;
for(int i=1;i<=n;i++)cin>>a[i];
memset(f,-0x3f3f,sizeof f);
deque<int>dq;
f[0][0]=0;
for(int i=1;i<=n;i++){
int l=1,r=1;
q[l]=f[i-1][w];
pos[l]=w;
for(int j=w;j;j--){
while(pos[l]>j+s-1&&l<=r)++l;
while(pos[l]>j+s-1 && l<=r) ++l;
while(q[r]<f[i-1][j-1] && l<=r)--r;
pos[++r]=j-1;
q[r]=f[i-1][j-1];
f[i][j]=q[l]+j*a[i];
}
}
int ans=-inf;
for(int i=0;i<=w;i++)ans=max(ans,f[n][i]);
cout<<ans<<endl;
return 0^0;
}