普通模型
1.摘花生——属性表示最大时
f[i][j]表示第i行第j列所在位置的所有方案的最大值(属性)
转换公式是根据倒数第二步如何到最后一步推出来的
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=100;
int n,m,f[N][N],w[N][N];
int main(){
int t;
cin>>t;
while(t--)
{
cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
cin>>w[i][j];
memset(f,0,sizeof f);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
f[i][j]=max(f[i][j-1],f[i-1][j])+w[i][j];//只能往下或者往右走
}
cout<<f[n][m]<<endl;
}
return 0;
}
2.最低通行费——属性表示最小时
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=100;
int n,m,f[N][N],w[N][N];
int main(){
cin>>n;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
cin>>w[i][j];
memset(f,0x3f,sizeof f);
f[1][1]=w[1][1];
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
if(i>1) f[i][j]=min(f[i][j],f[i-1][j]+w[i][j]);
if(j>1) f[i][j]=min(f[i][j],f[i][j-1]+w[i][j]);
}
cout<<f[n][n];
return 0;
}
双路径模型
1. 方格取数
DP做法
f[k][i][i1]表示第一条路径行数为i,第二条路径行数为i1;k为两条路径的行列之和,两条路径同时出发,所以两条路径的k是相同的。
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=101;
int n,m,f[N+N][N][N],w[N][N];
int main(){
cin>>n;
int a,b,c;
while(cin>>a>>b>>c,a||b||c) w[a][b]=c;
for(int k=2;k<=n+n;k++)
for(int i=1;i<=n;i++)
for(int i1=1;i1<=n;i1++)//k,i,i1取值范围
{
int j=k-i,j1=k-i1;
if(j>=1&&j<=n&&j1>=1&&j1<=n)//两条路径的列数取值范围
{
int t;
t=w[i][j];
if(i!=i1) t+=w[i1][j1];//没有走到同一个格子情况
for(int g=0;g<=1;g++)
for(int h=0;h<=1;h++)//两条路径状态转移的四种情况
f[k][i][i1]=max(f[k][i][i1],f[k-1][i-g][i1-h]+t);
}
}
cout<<f[n+n][n][n];
return 0;
}
网络流做法
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
const int N=1100,inf=1e8;
int h[N],e[N],ne[N],f[N],w[N],idx;
int q[N],d[N],incf[N],pre[N];
bool st[N];
int n,id[N][N];
int S=N-2,T=N-1;
void add(int a,int b,int c,int d){
e[idx]=b,ne[idx]=h[a],f[idx]=c,w[idx]=d,h[a]=idx++;
e[idx]=a,ne[idx]=h[b],f[idx]=0,w[idx]=-d,h[b]=idx++;
}
bool spfa(){
int hh=0,tt=0;
memset(d,-0x3f,sizeof d);
memset(incf,0,sizeof incf);
q[tt++]=S,d[S]=0,incf[S]=inf;
while(hh!=tt){
int t=q[hh++];
if(hh==N) hh=0;
st[t]=false;
for(int i=h[t];~i;i=ne[i]){
int ver=e[i];
if(d[ver]<d[t]+w[i]&&f[i]){
d[ver]=d[t]+w[i];
incf[ver]=min(f[i],incf[t]);
pre[ver]=i;
if(!st[ver]){
q[tt++]=ver;
if(tt==N) tt=0;
st[ver]=true;
}
}
}
}
return incf[T]>0;
}
ll EK(){
ll r=0,cost=0;
while(spfa()){
int t=incf[T];
r+=t,cost+=(t*d[T]);
for(int i=T;i!=S;i=e[pre[i]^1]){
f[pre[i]]-=t,f[pre[i]^1]+=t;
}
}
return cost;
}
int main(){
memset(h,-1,sizeof h);
cin>>n;
int a,b,c,cnt=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
id[i][j]=++cnt;
add(cnt*2,cnt*2+1,inf,0);
}
}
add(S,id[1][1]*2,2,0);
add(id[n][n]*2+1,T,2,0);
while(cin>>a>>b>>c,a){
add(id[a][b]*2,id[a][b]*2+1,1,c);
add(id[a][b]*2,id[a][b]*2+1,inf,0);
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(i<n) add(id[i][j]*2+1,id[i+1][j]*2,inf,0);
if(j<n) add(id[i][j]*2+1,id[i][j+1]*2,inf,0);
}
}
cout<<EK();
return 0;
}
2. 传纸条
和上题差不多,也是两条路径,只是这题两条路径不能相交,只能在起点和终点才可以走一起。
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=101;
int n,m,f[N+N][N][N],w[N][N];
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
cin>>w[i][j];
for(int k=2;k<=n+m;k++)
for(int i=1;i<=n;i++)
for(int i1=1;i1<=n;i1++)
{
int j=k-i,j1=k-i1;
if(j>=1&&j<=n&&j1>=1&&j1<=n)
{
int t;
t=w[i][j];
if(i!=i1||(k==2)||(k==n+m)) //可行情况枚举
{
t+=w[i1][j1];
for(int g=0;g<=1;g++)
for(int h=0;h<=1;h++)
f[k][i][i1]=max(f[k][i][i1],f[k-1][i-g][i1-h]+t);
}
}
}
cout<<f[n+n][n][n];
return 0;
}
网络流做法
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
// const int N=2e4+10,inf=1e8;
const int N=5500,M=(N*3+10)*2,inf=1e8;//算点数和边数尽量在不超内存的情况下算大一点
int h[N],e[M],ne[M],f[M],w[M],idx;
int q[N],d[N],incf[N],pre[N];
bool st[N];
int n,m;
int S=N-2,T=N-1;
void add(int a,int b,int c,int d){
e[idx]=b,ne[idx]=h[a],f[idx]=c,w[idx]=d,h[a]=idx++;
e[idx]=a,ne[idx]=h[b],f[idx]=0,w[idx]=-d,h[b]=idx++;
}
int get(int x,int y,int t){
return ((x-1)*m+y)*2+t;
}
bool spfa(){
int hh=0,tt=0;
memset(d,-0x3f,sizeof d);
memset(incf,0,sizeof incf);
q[tt++]=S,d[S]=0,incf[S]=inf;
while(hh!=tt){
int t=q[hh++];
if(hh==N) hh=0;
st[t]=false;
for(int i=h[t];~i;i=ne[i]){
int ver=e[i];
if(d[ver]<d[t]+w[i]&&f[i]){
d[ver]=d[t]+w[i];
incf[ver]=min(f[i],incf[t]);
pre[ver]=i;
if(!st[ver]){
q[tt++]=ver;
if(tt==N) tt=0;
st[ver]=true;
}
}
}
}
return incf[T]>0;
}
ll EK(){
ll r=0,cost=0;
while(spfa()){
int t=incf[T];
r+=t,cost+=(t*d[T]);
for(int i=T;i!=S;i=e[pre[i]^1]){
f[pre[i]]-=t,f[pre[i]^1]+=t;
}
}
return cost;
}
int main(){
memset(h,-1,sizeof h);
cin>>n>>m;
int a;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>a;
add(get(i,j,0),get(i,j,1),1,a);
}
}
add(get(1,1,0),get(1,1,1),inf,0);
add(get(n,m,0),get(n,m,1),inf,0);
add(S,get(1,1,0),2,0);
add(get(n,m,1),T,2,0);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(i<n) add(get(i,j,1),get(i+1,j,0),1,0);
if(j<m) add(get(i,j,1),get(i,j+1,0),1,0);
}
}
cout<<EK();
return 0;
}