【题解】P1004 方格取数

题面

题目传送门

前言

走迷宫 Promax,走两遍……

正文

动态规划

对于这种迷宫图的转移,往往有 \(dp_{i,j} = \max \lbrace dp_{i-1,j},dp_{i,j-1} \rbrace + mp_{i,j}\)

参考上述式子,注意到我们如果依照题意分两步实现的话,会存在后效性

所以考虑两次行走同时进行,即记录 \(dp_{i,j,k,l}\) 表示第一次走到 \((i,j)\),第二次走到 \((k,l)\) 的最大答案

转移方程形如 \(dp_{i,j,k,l} = \max \lbrace dp_{i-1,j,k-1,l},dp_{i-1,j,k,l-1},dp_{i,j-1,k-1,l},dp_{i,j-1,k,l-1} \rbrace + mp_{i,j}\)

但是还不完全对

题意要求取过的数不能再取——

也很好办,对于每一个 \((i,j)=(k,l)\),我们在 \(dp_{i,j,k,l}\) 上减去 \(mp_{i,j}\),即多计算的贡献

最小费用最大流

嗯——网络流简单建模捏

云落秒力

由于需要走两次

所以建原来的点对于新建的点要拆成两种决策

一条边容量为 1 ,花费为 \(-g\)

另一条容量为 1 ,花费为 0

要是走 \(k\) 遍的话就是连一条 \(-g\) 的,连 \(k-1\)\(0\)

每个点向相邻点(右和下)连容量为 \(inf\) 花费为 \(0\) 的边

跑费用流即可

代码

DP

#include<iostream>
using namespace std;
const int maxn=10;
int n,mp[maxn][maxn],dp[maxn][maxn][maxn][maxn];
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			int x,y,num;
			cin>>x>>y>>num;
			mp[x][y]=num;
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			for(int k=1;k<=n;k++){
				for(int l=1;l<=n;l++){
					dp[i][j][k][l]=max(dp[i-1][j][k-1][l],max(dp[i-1][j][k][l-1],max(dp[i][j-1][k-1][l],dp[i][j-1][k][l-1])))+mp[i][j]+mp[k][l];
					if(i==k&&j==l){
						dp[i][j][k][l]-=mp[i][j];
					}
				}
			}
		}
	}
	cout<<dp[n][n][n][n]<<endl;
	return 0;
}

费用流

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define x1 x_1
#define y1 y_1
#define x2 x_2
#define y2 y_2
#define endl '\n'
#define int long long
using namespace std;
const int maxn=16,maxm=1e6+10,inf=9e18;
int n,x,y,z,a[maxn][maxn];
int head[maxm],tot=1;
struct node{
	int to,nxt,flow,val;
}e[maxm];
int S,T,pre[maxm],last[maxm],fl[maxm],dis[maxm];
bool vis[maxm];
queue<int> q;
inline int id(int x,int y){
	return (x-1)*n+y;
}
inline void add(int u,int v,int fl,int w){
	e[++tot].to=v;
	e[tot].flow=fl;
	e[tot].val=w;
	e[tot].nxt=head[u];
	head[u]=tot;
	return;
}
inline void build(){
	S=0;
	T=2*n*n+1;
	add(S,id(1,1),inf,0);
	add(id(1,1),S,0,0);
	add(id(n,n)+n*n,T,inf,0);
	add(T,id(n,n)+n*n,0,0);
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			if(i+1<=n){
				add(id(i,j)+n*n,id(i+1,j),inf,0);
				add(id(i+1,j),id(i,j)+n*n,0,0);
			}
			if(j+1<=n){
				add(id(i,j)+n*n,id(i,j+1),inf,0);
				add(id(i,j+1),id(i,j)+n*n,0,0);
			}
			add(id(i,j),id(i,j)+n*n,1,0);
			add(id(i,j)+n*n,id(i,j),0,0);
			add(id(i,j),id(i,j)+n*n,1,-a[i][j]);
			add(id(i,j)+n*n,id(i,j),0,a[i][j]);
		}
	}
	return;
}
inline bool spfa(){
	memset(vis,false,sizeof(vis));
	memset(dis,0x7f,sizeof(dis));
	memset(fl,0x7f,sizeof(fl));
	q.push(S);
	dis[S]=0;
	vis[S]=true;
	pre[T]=-1;
	while(!q.empty()){
		int u=q.front();
		q.pop();
		vis[u]=false;
		for(int i=head[u];i;i=e[i].nxt){
			int v=e[i].to,f=e[i].flow,w=e[i].val;
			if(f&&dis[v]>dis[u]+w){
				dis[v]=dis[u]+w;
				pre[v]=u;
				last[v]=i;
				fl[v]=min(fl[u],f);
				if(!vis[v]){
					vis[v]=true;
					q.push(v);
				}
			}
		}
	}
	return ~pre[T];
}
inline int mcmf(){
	int res=0;
	while(spfa()){
		res+=(fl[T]*dis[T]);
		for(int i=T;i!=S;i=pre[i]){
			e[last[i]].flow-=fl[T];
			e[last[i]^1].flow+=fl[T];
		}
	}
	return res;
}
signed main(){
	//freopen(".in","r",stdin);
	//freopen(".out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin>>n;
	while(true){
		cin>>x>>y>>z;
		if(x==0&&y==0&&z==0){
			break;
		}
		a[x][y]=z;
	}
	build();
	int ans=mcmf();
	cout<<-ans<<endl;
	//fclose(stdin);
	//fclose(stdout);
	return 0;
}

后记

云落“唰”地秒,“嗖”地 \(A\)

不得不说,云落还是太巨啦

完结撒花!

posted @ 2024-12-19 15:18  sunxuhetai  阅读(15)  评论(1)    收藏  举报