[UVa-1347] Tour

DP+思维;模板类型

传送门:$>here<$

题意

n种长方体,各有无限个。可以横竖侧摆放。只有一个长方形的底面,长小于且宽小于令一个底面才可以叠在上面。问最多叠多高?

数据范围:$n \leq 30$

Solution

建模

将木块的6种状态(不是3种)作为不同物体考虑。若a能在b下面,那么连一条a->b的边,权值为b的高。至于第一块的高,考虑使用虚拟点。

求最长

已经将所有可能情况归结在图中了。显然这个图一定无环。所以利用DAG的性质,$O(n^2)$求最长路即可。

DAG求最长路的本质是DP。设$dp[i]$表示从$i$出发的最长路,由于一定不会有环,所以用所有$i$连出去的点进行转移即可。换句话说这些连出去的点和$i$毫无关系,是个独立的子问题。DAG里是可以有重复的子问题的(树就没有)

透过题解看本质

何时利用图建模

二元关系可以利用图来建模。在这道题中,一块木块能否放在另一块上面是一个二元关系。而在建模过程中,每一个木块的状态是唯一的,像这种木块翻转的应当看做两种情况。

隐式图

后来想想,其实这道题没有必要建图。因为我们可以对所有木块排序,排序完后就是一个类似LIS的问题了。

这么想来,$O(n^2)$的LIS做法其实也可以利用DAG来解决,即一个数在另一个数前面且小于后一个,那么连边。求最长路就是LIS。而我们并没有使用建图来解决LIS问题。

不过这给了我启示,其实DP就是DAG呀!所谓的无后效性,就是无环。

my code

有多种实现方法。最简单是记搜,此外填表法或刷表法都可以。其中记搜最好写,也最容易理解。

记忆化搜索

/*By DennyQi 2019*/
#include <cstdio>
#include <queue>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long ll;
const int MAXN = 1010;
const int MAXM = 20010;
const int INF = 0x3f3f3f3f;
inline int Max(const int a, const int b){ return (a > b) ? a : b; }
inline int Min(const int a, const int b){ return (a < b) ? a : b; }
inline int read(){
    int x = 0; int w = 1; register char c = getchar();
    for(; c ^ '-' && (c < '0' || c > '9'); c = getchar());
    if(c == '-') w = -1, c = getchar();
    for(; c >= '0' && c <= '9'; c = getchar()) x = (x<<3) + (x<<1) + c - '0'; return x * w;
}
struct Coordinate{
    double x,y;
}a[MAXN];
int n;
double dp[MAXN][MAXN];
bool vis[MAXN][MAXN];
inline bool cmp(const Coordinate& a, const Coordinate& b){
    return a.x < b.x;
}
inline double dist(int i, int j){
    double res = 0.0;
    res = sqrt((a[i].x-a[j].x)*(a[i].x-a[j].x) + (a[i].y-a[j].y)*(a[i].y-a[j].y));
    return res;
}
double DP(int a, int b){
    if(a < b) swap(a,b);
    if(a == n){
        return dist(b,n);
    }
    if(vis[a][b] || vis[b][a]) return dp[a][b];
    vis[a][b] = vis[b][a] = 1;
    dp[a][b] = min(DP(a+1,b)+dist(a,a+1), DP(a,a+1)+dist(b,a+1));
    dp[b][a] = dp[a][b];
    return dp[a][b];
}
int main(){
    freopen(".in","r",stdin);
    n = read();
    for(int i = 1; i <= n; ++i){
        scanf("%lf%lf",&a[i].x,&a[i].y);
    }
    sort(a+1,a+n+1,cmp);
    printf("%.2f", DP(1,1));
    return 0;
} 

刷表法

dp[1][1] = 0;
    for(int i = 1; i <= n; ++i){
        for(int j = i; j <= n; ++j){
            if(i==j && i>1 && i<n) continue;
            if(j < n){
                dp[i][j+1] = min(dp[i][j+1], dp[i][j] + dist(j,j+1));
                dp[j][j+1] = min(dp[j][j+1], dp[i][j] + dist(i,j+1));
            }
            else{
                dp[n][n] = dp[i][n] + dist(i,n);
            }
        }
    }

填表法

dp[1][1] = 0;
    for(int i = 1; i <= n; ++i){
        for(int j = i; j <= n; ++j){
            if(i==j && i<n) continue;
            dp[i][j] = dp[i][j-1] + dist(j-1,j);
            if(i+1 == j){
                for(int k = i-1; k >= 1; --k){
                    dp[i][j] = min(dp[i][j], dp[i][k] + dist(k,j));
                }
            }
            dp[j][i] = dp[i][j];
        }
    }

猜你喜欢

转载自www.cnblogs.com/qixingzhi/p/10372790.html
今日推荐