【算法竞赛入门经典例题题解】 【DP】练习城市里的间谍 A Spy in the Metro UVA1025 UVA437 巴比伦塔 刘汝佳

【算法竞赛入门经典例题题解】 【DP】

UVA1025 练习城市里的间谍 A Spy in the Metro

洛谷链接 UVA1025 练习城市里的间谍 A Spy in the Metro

题目

某城市地铁是一条直线,有 nn(2\leq n\leq 502≤n≤50)个车站,从左到右编号 …n。有 M1辆列车从第 1 站开始往右开,还有 M2 辆列车从第 n 站开始往左开。列车在相邻站台间所需的运行时间是固定的,因为所有列车的运行速度是相同的。在时刻 0,Mario 从第 1 站出发,目的在时刻 T( 2000≤T≤200)会见车站 n 的一个间谍。在车站等车时容易被抓,所以她决定尽量躲在开动的火车上,让在车站等待的时间尽量短。列车靠站停车时间忽略不计,且 Mario 身手敏捷,即时两辆方向不同的列车在同一时间靠站,Mario 也能完成换乘。

大意

就是你从车站1出发在时刻T到达车站n,要求你尽可能在车上,输出最小在车站等待时间

思路

  1. 首先要思考影响决策的因素,这道题中变量是时间T和车站n,因此我们可以定义dp[ i ] [ j ] 在 i 时刻中你在 j 车站 最小等待时间
  2. 接下来思考dp[ i ] [ j ] 这个集合的划分 ,也就是dp[ i ] [ j ] 是如何转化过来的 , 那么根据题意,如果说到达j车站,一,我在上一个时刻就在这个车站等待,二,我之前乘坐向右的车在 i 时刻到达 j ,三 我乘坐向左的车在 i 时刻到达 j ,那么我们就可以写出状态转移方程

dp[ i ] [ j ] = min(dp[ i -1] [ j ] ,dp[i + t[j-1]],dp[ i + t[j]] [ j +1 ] )

AC代码

#include
const int oo=0x3f3f3f3f;
using namespace std;
int n,T,M1,M2,kase;
int t[55];
bool trainl[55][10010];//train[i][j]:第i站第j时间是否有train
bool trainr[55][10010];
int dp[10010][55];//dp[i][j]:在时刻i你在车站j最低等多少时间
int main(){
    while(cin>>n && n!=0){
    kase++;
    cin>>T;
    memset(t, 0, sizeof(t));
    memset(trainl, 0, sizeof(trainl));
    memset(trainr, 0, sizeof(trainr));
    for(int i=1; i<=n-1; i++) cin>>t[i];//t[i]:i~i+1站时间
    cin>>M1;
    for(int i=1; i<=M1; i++){
        int t1;//左站出发时间
        cin>>t1;
        int sum=t1;
        for(int j=1; j<=n; j++){
            trainl[j][sum] = 1;
            sum+=t[j];
        }
    }
    cin>>M2;
    for(int i=1; i<=M2; i++){
        int t2;//右站出发时间
        cin>>t2;
        int sum=t2;
        for(int j=n; j>=1; j--){
            trainr[j][sum] = 1;
            sum+=t[j-1];
        }
    }
    for(int i=1; i<=n-1; i++)
        dp[T][i]=0x3f3f3f3f;
    dp[T][n] = 0;
    for(int i=T-1; i>=0; i--)
        for(int j=1; j<=n; j++){
            dp[i][j]=dp[i+1][j]+1;
            if(j<n && trainl[j][i] && i+t[j]<=T )
                dp[i][j] = min(dp[i][j], dp[i+t[j]][j+1]);
            if(j>1 && trainr[j][i] && 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] >= oo) cout<< "impossible\n";
    else cout<<dp[0][1] <<"\n";
    }
    return 0;
}

UVA437 巴比伦塔 The Tower of Babylon

洛谷链接 UVA437 巴比伦塔

题意

定义一种塔,只有一个方块的底的两条边严格小于另一个方块的底的两条边,这个方块才能堆在另一个上面。方块可以任意旋转。给出n个方块包含三个尺寸(a , b , c) ,问最高的塔高度是多少

思路(DAG上动态规划)

假设我们先不管旋转的问题,考虑每个方块是否能叠起来的问题,那么只有宽严格大于并且长严格大于另一个合法,我们把这种关系变成图上的单项边,把高作为边上的权值,在寻找最高塔的过程中就是找最长路 ,设dp[ i ]为以i为起点的最长路
那么 dp[ i ] = max(dp[j]+1) ( i与j有边)
再考虑旋转的问题,其实就是把一个立方体变成三个立方体,长宽高轮转一下
还有一些细节: 如何建边 1 . 根据上述的关系 构造出一个图来 2 . 一边dp一边暴力判断是否有边(我是这么写的,代码量少)

AC代码

#include
using namespace std;
const int N = 1e6+7;
typedef long long ll;
struct cube{
	int x,y,z;
	bool operator > (const cube a)const {
		return (x>a.x&&y>a.y)||(x>a.y&&y>a.x);
	}// 判断边
}A[1000];
int cnt_cube = 0;
int dp[1000];
int dfs(int ix){ //立方体下标 
	if(dp[ix]>0) return dp[ix];
	dp[ix] = A[ix].z;
	for(int i = 0;i<cnt_cube;i++){
		if(A[ix]>A[i]){
			dp[ix] = max(dp[ix],dfs(i)+A[ix].z);
		}
	} 
	return dp[ix];
}
int main(){
	int n;
	int cass = 1;
	while(cin>>n,n){ 
		cnt_cube = 0;
		for(int i = 0;i<n;i++){
			int a,b,c;
			cin>>a>>b>>c;
			A[cnt_cube++] = {a,b,c};
			A[cnt_cube++] = {a,c,b};
			A[cnt_cube++] = {b,c,a}; 
		} 
		int res = -1;
		memset(dp,0,sizeof(dp));
		for(int i = 0;i<cnt_cube;i++){ // 以i为起点的最大高度 	
			res = max(res,dfs(i));
		}
		printf("Case %d: maximum height = %d\n",cass++,res);
	}
	return 0;
}

你可能感兴趣的:(算法,c语言,动态规划)