int solve()
{
for(int i=1;i<=n;i++) d[n][i]=a[n][i];//初值
//变量枚举
for(int i=n-1;i>=1;i--)
for(int j=1;j<=i;j++)
d[i][j]=max(d[i+1][j],d[i][j+1])+a[i][j];
}
模型:不固定起点与终点的DAG(有向无环图)模型
先根据关系建有向图(G[i][j]=1,表示矩形i可以嵌套在矩形j)
描述:有n个矩形,每个矩形可以用a,b来描述,表示长和宽。矩形X(a,b)可以嵌套在矩形Y(c,d)中当且仅当a < c,b < d或者b < c,a < d(相当于旋转X90度)。例如(1,5)可以嵌套在(6,2)内,但不能嵌套在(3,4)中。你的任务是选出尽可能多的矩形排成一行,使得除最后一个外,每一个矩形都可以嵌套在下一个矩形内。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
typedef long long ll;
#define INF (1<<31)-1
using namespace std;
const int maxn=1005;
int n;
struct node
{
int x,y;
}tri[maxn];
int G[maxn][maxn];//存储DAG
int dp[maxn];
bool check(struct node a,struct node b)
{ //嵌套判断
return ((a.xint DP(int i)
{
int& ans=dp[i];
if(ans>0) return ans;
ans=1;
for(int j=1;j<=n;j++)
if(G[i][j]) ans=max(ans,DP(j)+1);
return ans;
}
int main()
{
int N;
cin>>N;
while(N--)
{
memset(G,0,sizeof(G));
memset(dp,0,sizeof(dp));
cin>>n;
for(int i=1;i<=n;i++)
cin>>tri[i].x>>tri[i].y;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(check(tri[i],tri[j]))
G[i][j]=1;
int ans=-1;
for(int i=1;i<=n;i++)
DP(i);
for(int i=1;i<=n;i++)
ans=max(dp[i],ans);
cout<return 0;
}
问题描述:有n中硬币,面值分别为V1,V2……Vn,每种都有无限多。给定非负整数S,可以选用多少个硬币,使得面值之和恰为S?输出硬币数目的最小值和最大值。
模型是固定终点的最长路和最短路
//设置合理的边界条件
minv[0]=maxv[0]=0;
for(int i=1;i<=S;i++){
minv[i]=INF; maxv[i]=-INF;
}
for(int i=1;i<=S;i++)
for(int j=1;j<=n;j++)
if(i>=V[j]){
minv[i]=min(minv[i],minv[i-V[j]]+1);
maxv[i]=max(maxv[i],maxv[i-V[j]]+1);
}
if(minv[0]==INF||maxv[0]==-INF) printf("-1");//无解
else printf("%d %d",minv[S],maxv[S]);
分析:题目有两个关键量:时间和地点,按照题目描述我们可以设置d(i,j)表示时刻i,在车站j,最少还要等待多长时间。很明显每一个状态都有三个决策
其中t[j]标识从j站台开往j+1站台所需的时间
接下来确定递推方向直观的我们要求的是dp[0][1] 而且从状态转移方程中我们也可以看出是逆着时间的顺序递推的
那么边界就可以确定了
d(T,n)=0;其他d(T,i)=INF;
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
typedef long long ll;
#define INF (1<<20)-1
using namespace std;
int t[55];//各站点行驶时间
int d1[55],d2[55];//记录向左出发的车的时间,向右出发车的时间
int dp[205][55];
int has_train[205][55][2];//时刻T在站点i是否有向左开,或向右开的车
int n,T;
int main()
{
int Kase=0;
while(cin>>n&&n) {
memset(has_train,0,sizeof(has_train));
cin>>T;
for(int i=1; i<=n-1; i++)
cin>>t[i];//各个站点行驶时间
int m1,m2;
cin>>m1;
for(int i=1; i<=m1; i++) { //向右出发
int time=0;
cin>>d1[i];
has_train[d1[i]][1][0]=1;//第一个站点是否有车看车的出发时间
for(int j=1; j//处理has_train数组
time+=t[j];
has_train[time+d1[i]][j+1][0]=1;
}
}
cin>>m2;
for(int i=1; i<=m2; i++) { //向左出发
int time=0;
cin>>d2[i];
has_train[d2[i]][n][1]=1;//第n个站点是否有车看车的出发时间
for(int j=n-1;j>1;j--) {
//处理has_train数组
time+=t[j];
has_train[time+d2[i]][j][1]=1;//注意车站编号从左向右递增
}
}
//设置边界
dp[T][n]=0;
for(int i=1; i<=n-1; i++) //由于最终要求的是dp[0][1];递推方向是沿着时间逆序,故边界设置如此
dp[T][i]=INF;
for(int i=T-1; i>=0; i--)
for(int j=1; j<=n; j++) {
dp[i][j]=dp[i+1][j]+1;//没有车子可乘等待1
if(j0]&&i+t[j]<=T)
dp[i][j]=min(dp[i][j],dp[i+t[j]][j+1]);//搭乘向右的车
if(j>1&&has_train[i][j][1]&&i+t[j-1]<=T)
dp[i][j]=min(dp[i][j],dp[i+t[j-1]][j-1]);//搭乘向左的车
}
cout<<"Case Number "<<++Kase<<": ";
if(dp[0][1]>=INF) cout<<"impossible"<else cout<0][1]<return 0;
}
题意:有n( n≤30 )种立方体,每种都有无穷个,要去选一些立方体摞成一根尽量高的柱子(可以自行选择哪一条边坐高),使得每个立方体的地面长宽分别严格小于它下方立方体的地面长宽。
分析:类似与嵌套矩形属于DAG最长路的模型。需要注意的是每一组数据a,b,c可能组成三种立方体。
d(i)是以i为顶端的柱子高度,状态转移方程为d(i)=max{(d(j)+h(i)|(i,j)∈E}
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
typedef long long ll;
#define INF (1<<31)-1
using namespace std;
const int maxn=30*3+3;
struct node {
int x,y,z;
} blo[maxn];
int dp[maxn];
int n;
int check(struct node a,struct node b)
{
//a是否可以堆积在b上
if((a.x>b.x&&a.y>b.y)||(a.y>b.x&&a.x>b.y))
return 1;
return 0;
}
int DP(int a)
{
if(dp[a]>0) return dp[a];
dp[a]=blo[a].z;
for(int i=1; i<=3*n; i++) {
if(check(blo[a],blo[i]))
dp[a]=max(dp[a],DP(i)+blo[a].z);
}
return dp[a];
}
int main()
{
int Kase=0;
while(cin>>n&&n) {
memset(dp,0,sizeof(dp));
int cur=1;
for(int i=1; i<=n; i++) {
int a,b,c;
cin>>a>>b>>c;
blo[cur].x=a;
blo[cur].y=b;
blo[cur++].z=c;
blo[cur].x=a;
blo[cur].y=c;
blo[cur++].z=b;
blo[cur].x=b;
blo[cur].y=c;
blo[cur++].z=a;
}
for(int i=1; i<=3*n; i++)
DP(i);
int mmax=-1;
for(int i=1; i<=3*n; i++)
mmax=max(mmax,dp[i]);
printf("Case %d: maximum height = %d\n",++Kase,mmax);
}
return 0;
}
题意:给一个n行m列( m≤10,n≤100 )的整数矩阵,从第一列任何一个位置出发每次往右,右上或右下走一格,最终走到最后一列。要求经过的整数之和最小。整个矩阵是环行的,即第一行的上一行是最后一行,最后一行的下一行是第一行。输出路径上每列的行号。多解时,输出字典序最小的。
思路:这是个明显的多阶段决策问题。
状态d(i,j)为从格子(i,j)出发到最后一列的最小开销。状态转移方程为d(i,j)=max{d(i,j−1),d(i−1,j−1),d(i+1,j+1)}+a[i][j]; 递推方向显然是逆推.。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
typedef long long ll;
#define INF (1<<30)-1
using namespace std;
const int maxn=105;
int G[maxn][maxn];
int d[maxn][maxn];
int nnext[maxn][maxn];
int n,m;
int main()
{
while(cin>>m>>n) {
for(int i=0; ifor(int j=0; jcin>>G[i][j];
int ans=INF;
int first=0;
for(int j=n-1; j>=0; j--) {
for(int i=0; iif(j==n-1) d[i][j]=G[i][j];//设置边界
else {
int rows[3]= {i,i-1,i+1}; //这是之前不知道的用法
if(i==0) rows[1]=m-1; //设置环行 第0行的上一行是m-1
if(i==m-1) rows[2]=0; //第m-1行的下一行是第0行
sort(rows,rows+3);//保持字典序最小
d[i][j]=INF;
for(int k=0; k<3; k++) {
int v=d[rows[k]][j+1]+G[i][j];
if(v//记录当前位置的下一个位置的行
}
}
}
if(j==0&&d[i][j]//特别找出第一列最优的行号
first=i;
}
}
}
printf("%d",first+1);
for(int i=nnext[first][0],j=1;j//更新输出下一个行号
printf(" %d",i+1);
printf("\n%d\n",ans);
}
return 0;
}