庆祝该系列突破 80 期!!!1
记忆化搜索
dp 的一种 dfs 实现。
P1434
令 \(dp_{i,j}\) 表示以 \((i,j)\) 结束的最长滑坡的长度。
答案:\(\max\{dp_{i,j}\}\)。
初始:\(dp_{i,j}=1\)。
转移:\(dp_{i,j}=dp_{x,y}+1\),其中 \((x,y)\) 为 \((i,j)\) 四个方向上的邻接点。
实现时枚举每个点进行记忆化搜索即可。
code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=5e2+5;
const int dx[]={1,0,-1,0};
const int dy[]={0,1,0,-1};
int r,c;
char mp[N][N];
bool vis[N][N];
int get(){
int sum=0;
for(int i=1;i<=r;i++)
for(int j=1;j<=c;j++)
if(mp[i][j]=='0'&&!vis[i][j])
sum++;
return sum;
}
void dfs(int x,int y){
if(x<1||x>r||y<1||y>c||vis[x][y]||mp[x][y]!='0')
return;
vis[x][y]=1;
for(int i=0;i<4;i++){
int xx=x+dx[i],yy=y+dy[i];
dfs(xx,yy);
}
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cin>>r>>c;
for(int i=1;i<=r;i++)
for(int j=1;j<=c;j++)
cin>>mp[i][j];
int ans=1e9;
for(int i=1;i<=c;i++){
dfs(1,i);
dfs(r,i);
}
for(int i=1;i<=r;i++){
dfs(i,1);
dfs(i,c);
}
cout<<get();
return 0;
}
P1506
从边缘每个点开始 flood-fill 即可,注意边上的点有可能不能作为源点。
code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=5e2+5;
const int dx[]={1,0,-1,0};
const int dy[]={0,1,0,-1};
int r,c;
char mp[N][N];
bool vis[N][N];
int get(){
int sum=0;
for(int i=1;i<=r;i++)
for(int j=1;j<=c;j++)
if(mp[i][j]=='0'&&!vis[i][j])
sum++;
return sum;
}
void dfs(int x,int y){
if(x<1||x>r||y<1||y>c||vis[x][y]||mp[x][y]!='0')
return;
vis[x][y]=1;
for(int i=0;i<4;i++){
int xx=x+dx[i],yy=y+dy[i];
dfs(xx,yy);
}
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cin>>r>>c;
for(int i=1;i<=r;i++)
for(int j=1;j<=c;j++)
cin>>mp[i][j];
int ans=1e9;
for(int i=1;i<=c;i++){
dfs(1,i);
dfs(r,i);
}
for(int i=1;i<=r;i++){
dfs(i,1);
dfs(i,c);
}
cout<<get();
return 0;
}
P4017
令 \(dp_i\) 表示以 \(i\) 结尾的最长食物链的长度。
答案:所有出度为 \(0\) 的点 \(i\) 的 \(\sum dp_i\)。
初始:所有入度为 \(0\) 的点 \(i\) 有 \(dp_i=1\)。
转移:\(dp_i=dp_{cur}+1\)。
注意建反边,为了方便找转移点。
(记搜的题目一般都是从终止节点开始 dfs,因此一般要建反边)
code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=5e3+5;
const int MOD=80112002;
int n,m;
int dp[N],in[N],out[N];
vector<int> G[N];
int dfs(int cur){
if(dp[cur]!=0)
return dp[cur]%MOD;
if(!in[cur])
return dp[cur]=1;
for(int i:G[cur])
dp[cur]=(dp[cur]+dfs(i))%MOD;
return dp[cur]%MOD;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cin>>n>>m;
for(int i=1,u,v;i<=m;i++){
cin>>u>>v;
G[v].push_back(u);
in[v]++,out[u]++;
}
int ans=0;
for(int i=1;i<=n;i++)
if(!out[i])
ans=(ans+dfs(i))%MOD;
cout<<ans;
return 0;
}
P6145
令 \(dp_i\) 表示第 \(i\) 次挤奶的最早时间。
答案:所有 \(dp_i\)。
初始:\(dp_i=s_i\)。
转移:\(dp_i=\max(dp_i,dp_{cur}+w)\)(注意是取 \(\max\),因为要保证所有的邻接点要求均满足,当然必须得满足挤奶时间最晚的那个了)。
code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+5;
int n,m,c;
int dp[N];
bool vis[N];
struct Edge{
int v,w;
};
vector<Edge> G[N];
int dfs(int cur){
if(vis[cur])
return dp[cur];
vis[cur]=1;
for(auto i:G[cur])
dp[cur]=max(dp[cur],dfs(i.v)+i.w);
return dp[cur];
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cin>>n>>m>>c;
for(int i=1;i<=n;i++)
cin>>dp[i];
for(int i=1,u,v,w;i<=c;i++){
cin>>u>>v>>w;
G[v].push_back({u,w});
}
for(int i=1;i<=n;i++)
cout<<dfs(i)<<'\n';
return 0;
}
P1775
典。略。
code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=3e2+5;
int n,m,c;
int a[N],dp[N][N],s[N];
bool vis[N][N];
struct Edge{
int v,w;
};
vector<Edge> G[N];
int dfs(int l,int r){
if(vis[l][r])
return dp[l][r];
vis[l][r]=1;
for(int i=l;i<r;i++)
dp[l][r]=min(dp[l][r],dfs(l,i)+dfs(i+1,r)+s[r]-s[l-1]);
return dp[l][r];
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cin>>n;
memset(dp,0x3f,sizeof dp);
for(int i=1;i<=n;i++)
cin>>a[i],s[i]=s[i-1]+a[i],dp[i][i]=0;
cout<<dfs(1,n);
return 0;
}
P2217
挺有意思一题。
发现我们关心的东西:当前矩阵是啥,以及分割成了几个矩阵。
当前矩阵需要使用四个坐标描述,因此我们有状态:
令 \(dp_{x,y,xx,yy,k}\) 表示当前矩阵对角顶点为 \((x,y),(xx,yy)\),且已分割了 \(k\) 个矩阵的平方和(\(\sum^n_{i=1} (a_i-avg)^2\))最小值。
值得一提的是,序列 \(a\) 的均方差为:
其中:
然后记忆化搜索即可,转移自己画一下图就能推出来了,注意两部分拼起来的时候不要把平方和再加一遍。
code
#include<bits/stdc++.h>
using namespace std;
const int N=11;
int a,b,n;
double avg;
int scr[N][N];
double dp[N][N][N][N][N];
double get(int x,int y,int xx,int yy){
int res=0;
for(int i=x;i<=xx;i++)
for(int j=y;j<=yy;j++)
res+=scr[i][j];
return (res-avg)*(res-avg);
}
double dfs(int x,int y,int xx,int yy,int k){
if(k==1)
return dp[x][y][xx][yy][k]=get(x,y,xx,yy);
if(dp[x][y][xx][yy][k])
return dp[x][y][xx][yy][k];
dp[x][y][xx][yy][k]=1e9;
for(int i=x;i<xx;i++)
for(int j=1;j<k;j++)
dp[x][y][xx][yy][k]=min(dp[x][y][xx][yy][k],dfs(x,y,i,yy,j)+dfs(i+1,y,xx,yy,k-j));
for(int i=y;i<yy;i++)
for(int j=1;j<k;j++)
dp[x][y][xx][yy][k]=min(dp[x][y][xx][yy][k],dfs(x,y,xx,i,j)+dfs(x,i+1,xx,yy,k-j));
return dp[x][y][xx][yy][k];
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cin>>a>>b>>n;
for(int i=1;i<=a;i++)
for(int j=1;j<=b;j++)
cin>>scr[i][j];
for(int i=1;i<=a;i++)
for(int j=1;j<=b;j++)
avg+=scr[i][j];
avg/=n;
cout<<setprecision(2)<<fixed<<sqrt(dfs(1,1,a,b,n)/(double)n);
return 0;
}