数字三角形模型
母题 : 数字三角形
思路
集合 f [i] [j] 表示所有从起点走到(i,j)的路径
属性 f [i] [j] 存的数是集合中所有路径和的最大值
状态计算:对于每一条从起点到 ( i , j ) 的路径,其要么是从左上方来的,要么是从右上方来的。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+10;
int n;
int a[510][510];
int f[510][510];
signed main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++)
for(int j=1;j<=i;j++) cin>>a[i][j];
for(int i=0;i<=n;i++)
for(int j=0;j<=i+1;j++) f[i][j]=-1e9;
f[1][1]=a[1][1];
for(int i=2;i<=n;i++)
for(int j=1;j<=i;j++)
f[i][j]=max(f[i-1][j],f[i-1][j-1])+a[i][j];
int mmax=-1e9;
for(int i=1;i<=n;i++) mmax=max(mmax,f[n][i]);
cout<<mmax<<endl;
return 0;
}
子题1 : 摘花生
思路
基本和母题的思路一致,转移方程一致
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e3+10;
int f[N][N];
int w[N][N];
void solve()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++)
cin>>w[i][j];
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++)
f[i][j]=max(f[i-1][j],f[i][j-1])+w[i][j];
}
cout<<f[n][m]<<endl;
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
int T=1;
cin>>T;
while(T--)
{
solve();
}
return 0;
}
子题2 : 最低通行费
思路
因为必须在(2*N-1)个单位时间穿越过来,所以只能向右或向左移动
集合 f [i] [j] 表示走到第i行第j列的所有方案
属性 走过路线的总价值最小值Min
转移方程
从左面走过来 从右面走过来
f [i] [j] = f [i] [j-1] +w f [i] [j] = f [i-1] [j] +w
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e3+10;
const int inf = 1e9;
int w[N][N];
int f[N][N];
void solve()
{
int n;
cin>>n;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++)
cin>>w[i][j];
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(i==1&&j==1) f[i][j]=w[i][j];
else
{
f[i][j]=inf;
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]<<endl;
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
int T=1;
// cin>>T;
while(T--)
{
solve();
}
return 0;
}
子题3 :方格取数
思路
和之前的题不同是本题需要走两边
如果只走一次的话,求从起点到终点的最大权值和应该为:f[i] [j] = max(f[i-1] [j], f[i][j-1])+w[i] [j]; 即f[i][j]表示从起点走到(i,j)的最大权值和,
而本题是从起点走两次走到终点的最大权值和,即状态表示为:f[i1] [j1] [i2] [j2];这里,我们考虑假设两条路线是同时走的,则表示为:所有从起点(1,1)(1,1)出发,然后分别走到(i1,j1),(i2,j2)的最大权值和
我们可以进行 三维优化
状态表示 f [k] [x1] [x2]
集合 同时走K步,所有从(1,1),(1,1)走到 (x1,y1),(x2,y2) 获得花生的数目
属性 Max
状态计算 左 左 f [k-1] [x1-1] [x2-1]
左 上 f [k-1] [x1-1] [x2]
上 左 f [k-1] [x1] [x2-1]
上 上 f [k-1] [x1] [x2]
特判 两个点是否重合 即 x1==x2 如果重合只加w [x1] [j1] 如果不重合加 w [x1] [j1] +w [x2] [j2]
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=15;
int w[N][N];
int f[N*2][N][N];
void solve()
{
int n; cin>>n;
int a,b,c;
while(cin>>a>>b>>c,a||b||c) w[a][b]=c;
for(int k=2;k<=n*2;k++){
for(int i1=1;i1<=n;i1++){
for(int i2=1;i2<=n;i2++){
int j1=k-i1,j2=k-i2;
if(j1>=1&&j1<=n&&j2>=1&&j2<=n){
int t=w[i1][j1];
if(i1!=i2) t+=w[i2][j2];
int &x=f[k][i1][i2];
x=max(x,f[k-1][i1-1][i2-1]+t);
x=max(x,f[k-1][i1-1][i2]+t);
x=max(x,f[k-1][i1][i2-1]+t);
x=max(x,f[k-1][i1][i2]+t);
}
}
}
}
cout<<f[n*2][n][n];
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
int T=1;
// cin>>T;
while(T--)
{
solve();
}
return 0;
}
子题4 : 传纸条
思路
可以思考成和方格取数 相同的代码逻辑,一个从上向下走,一个从下向上走,可以理解为两条路从上向下走,加上其限制条件
状态表示 f [k] [x1] [x2]
集合 同时走K步,所有从(1,1),(1,1)走到 (x1,y1),(x2,y2) 得到权值的最大值
状态计算
左 左 f [k-1] [x1-1] [x2-1]
左 上 f [k-1] [x1-1] [x2]
上 左 f [k-1] [x1] [x2-1]
上 上 f [k-1] [x1] [x2]
注意 不能走到一个格子里 (和方格取数不同的地方) ,因为最优解一定不会相交,一定可以绕开这个点向其他地方走
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=55;
int w[N][N];
int f[N*2][N][N]; //表示两条路径分别到达(i,k-i),(j,k-j);
void solve()
{
int n,m; 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<k;i++){
for(int j=1;j<k;j++){
int &v=f[k][i][j];
int tmp=w[i][k-i];
if(i!=j) tmp+=w[j][k-j];
v=max(v,f[k-1][i-1][j]);
v=max(v,f[k-1][i-1][j-1]);
v=max(v,f[k-1][i][j-1]);
v=max(v,f[k-1][i][j]);
v+=tmp;
}
}
}
cout<<f[n+m][n][n];
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
int T=1;
// cin>>T;
while(T--)
{
solve();
}
return 0;
}
标签:动态,int,模型,cin,long,solve,x2,三角形,x1
From: https://www.cnblogs.com/gloria-wmh/p/18387559