【题解】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\) 力
不得不说,云落还是太巨啦
完结撒花!