Living-Dream 系列笔记 第81期

Posted on 2024-10-09 20:22  _XOFqwq  阅读(5)  评论(0编辑  收藏  举报

庆祝该系列突破 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\) 的均方差为:

\[\sqrt{\frac{\sum^n_{i=1} (a_i-avg)^2}{n}}\\ \]

其中:

\[avg=\frac{\sum^n_{i=1} a_i}{n} \]

然后记忆化搜索即可,转移自己画一下图就能推出来了,注意两部分拼起来的时候不要把平方和再加一遍。

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;
}