zoukankan      html  css  js  c++  java
  • bzoj4244 & loj2878. 「JOISC 2014 Day2」邮戳拉力赛 括号序列+背包

    题目传送门

    https://lydsy.com/JudgeOnline/problem.php?id=4244

    https://loj.ac/problem/2878

    题解

    挺妙的一道题。

    一开始一直往最短路上面想,然后怎么想发现都没有用。

    然后就又开始自闭了。最后又去拜读题解了。(今天怎么读了两次题解啊,没救了没救了

    一条合法的路线一定是从 (0)(n + 1) 的链上套了无数个环。每一个邮戳台至少被一个环经过。

    经过邮戳台的方式有 (4) 类:

    1. 上行 -> 邮戳台 -> 下行,费用为 (u+e)
    2. 下行 -> 邮戳台 -> 上行,费用为 (d+v)
    3. 上行 -> 邮戳台 -> 上行,费用为 (u+v)
    4. 下行 -> 邮戳台 -> 下行,费用为 (d+e)

    其中第一类和第二类可以互相之间构成大环(跨越了两个邮戳台为大环)。

    对于一个环,可以发现从路程上,第一类一定出现在第二类的前面。但是,从位置上,第二类一定出现在第一类的前面。于是,我们令 (() 表示第二类环,()) 表示第一类环。于是合法的路径是一个合法的括号序列。另外,对于第 (4) 类,因为是从下行台来的,所以之前必须有一个 (()

    然后,令 (dp[i][j]) 表示前 (i) 个位置,有 (j) 个不匹配的 (() 的最优解。直接转移就可以了。

    注意第一二种情况可以多次使用,所以是一个完全背包。

    最后加上从 (0)(n + 1) 的链的长度。


    代码如下,时间复杂度 (O(n^2))

    #include<bits/stdc++.h>
    
    #define fec(i, x, y) (int i = head[x], y = g[i].to; i; i = g[i].ne, y = g[i].to)
    #define dbg(...) fprintf(stderr, __VA_ARGS__)
    #define File(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
    #define fi first
    #define se second
    #define pb push_back
    
    template<typename A, typename B> inline char smax(A &a, const B &b) {return a < b ? a = b, 1 : 0;}
    template<typename A, typename B> inline char smin(A &a, const B &b) {return b < a ? a = b, 1 : 0;}
    
    typedef long long ll; typedef unsigned long long ull; typedef std::pair<int, int> pii;
    
    template<typename I> inline void read(I &x) {
    	int f = 0, c;
    	while (!isdigit(c = getchar())) c == '-' ? f = 1 : 0;
    	x = c & 15;
    	while (isdigit(c = getchar())) x = (x << 1) + (x << 3) + (c & 15);
    	f ? x = -x : 0;
    }
    
    const int N = 3000 + 7;
    const int INF = 0x3f3f3f3f;
    
    int n, t;
    int u[N], v[N], d[N], e[N];
    int dp[N][N];
    
    inline void work() {
    	memset(dp, 0x3f, sizeof(dp)), dp[0][0] = 0;
    	for (int i = 1; i <= n; ++i) {
    		const int &u = ::u[i], &v = ::v[i], &d = ::d[i], &e = ::e[i];
    		for (int j = 0; j < n; ++j) smin(dp[i][j], dp[i - 1][j + 1] + u + e);
    		for (int j = 0; j <= n; ++j) smin(dp[i][j], dp[i - 1][j] + u + v);
    		for (int j = 1; j <= n; ++j) smin(dp[i][j], dp[i - 1][j - 1] + v + d);
    		for (int j = 1; j <= n; ++j) smin(dp[i][j], dp[i - 1][j] + d + e);
    		for (int j = 1; j <= n; ++j) smin(dp[i][j], dp[i][j - 1] + v + d);
    		for (int j = n - 1; ~j; --j) smin(dp[i][j], dp[i][j + 1] + u + e);
    		for (int j = 0; j <= n; ++j) if (dp[i][j] != INF) dp[i][j] += t * j * 2;
    	}
    	printf("%d
    ", dp[n][0] + (n + 1) * t);
    }
    
    inline void init() {
    	read(n), read(t);
    	for (int i = 1; i <= n; ++i) read(u[i]), read(v[i]), read(d[i]), read(e[i]);
    }
    
    int main() {
    #ifdef hzhkk
    	freopen("hkk.in", "r", stdin);
    #endif
    	init();
    	work();
    	fclose(stdin), fclose(stdout);
    	return 0;
    }
    
    
  • 相关阅读:
    vscode安装
    Linux下 Python绘图与可视化 及matplotlib与_tkinter安装
    C语言-结构体定义的几种方式
    leetcode- 88. 合并两个有序数组
    leetcode-16. 最接近的三数之和
    Leetcode-15. 三数之和
    Leetcode-561. 数组拆分 I
    Windows_pycharm下安装numpy
    python实现两个两个的翻转字符串
    linux复制文件夹、重命名文件夹、删除文件夹
  • 原文地址:https://www.cnblogs.com/hankeke/p/bzoj4244.html
Copyright © 2011-2022 走看看