Postal Vans

ACM South Pacific Region -- 2003

Tiring of their idyllic fields, the cows have moved to a new suburb. The suburb is a rectangular grid of streets with a post office at its Northwest corner. It has four avenues running East-West and N (1 <= N <= 1000) streets running North-South.

For example, the following diagram shows such a suburb with N=5 streets, with the avenues depicted as horizontal lines, and the post office as a dark blob at the top-left corner:

Each day the postal van leaves the post office, drives around the suburb and returns to the post office, passing exactly once through every intersection (including those on borders or corners). The executives from the post company want to know how many distinct routes can be established for the postal van (of course, the route direction is significant in this count).

For example, the following diagrams show two such routes for the above suburb:

As another example, the following diagrams show all the four possible routes for a suburb with N=3 streets:

Write a program that will determine the number of such distinct routes given the number of streets.

PROGRAM NAME: vans

INPUT FORMAT

  • Line 1: A single integer, N

SAMPLE INPUT (file vans.in)

4

OUTPUT FORMAT

  • Line 1: A single integer that tells how many possible distinct routes corresponding to the number of streets given in the input.

SAMPLE OUTPUT (file vans.out)

12

————————————————————————————————题解

又到了看一大面的英文Analysis然后翻译的时候了

先说说网上的做法【毕竟想看Analysis还得把题给过掉而且我不会做这道题】

这里有详细推导

这个看着还是比较亲切的,比什么插头dp要好多了【我也不会啊】

附个我的代码

 /*
ID: ivorysi
LANG: C++
PROG: vans
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <set>
#include <vector>
#define siji(i,x,y) for(int i=(x);i<=(y);++i)
#define gongzi(j,x,y) for(int j=(x);j>=(y);--j)
#define xiaosiji(i,x,y) for(int i=(x);i<(y);++i)
#define sigongzi(j,x,y) for(int j=(x);j>(y);--j)
#define inf 0x5f5f5f5f
#define ivorysi
#define mo 97797977
#define hash 974711
#define base 47
#define fi first
#define se second
#define pii pair<int,int>
#define esp 1e-8
typedef long long ll;
using namespace std;
struct bignum {
vector<int> s; bignum operator =(string x) {
s.clear();
gongzi(i,x.length()-,) {
s.push_back(x[i]-'');
}
return *this;
}
bignum operator =(long long x) {
s.clear();
do {
s.push_back(x%);
x/=;
}while(x>);
return *this;
}
bignum(string x) {
*this=x;
}
bignum(long long x=0LL) {
*this=x;
}
bignum operator + (const bignum b) const {
bignum c;
c.s.clear();
for(int g=,k=;;++k) {
if(g== && k>=b.s.size() && k>=s.size()) {break;}
int x=g;
if(k<b.s.size()) x+=b.s[k];
if(k<s.size()) x+=s[k];
c.s.push_back(x%);
g=x/;
}
return c;
}
bignum operator - (const bignum b) const {
bignum c;
c.s.clear();
for(int g=,k=;;++k) {
if(g== && k>=s.size()) {break;}
int x=s[k]-g;
if(k<b.s.size()) x-=b.s[k];
if(x<) {g=;x+=;}
else g=;
c.s.push_back(x);
}
return c;
}
bignum operator * (const bignum b) const {
bignum c;
int g=,x;
c.s.clear();
siji(i,,s.size()+b.s.size()+) c.s.push_back();
for(int i=;i<s.size();++i) {
for(int j=;j<b.s.size();++j) {
x=s[i]*b.s[j]+g+c.s[i+j];
c.s[i+j]=x%;
g=x/;
}
while(g!=) {c.s[i+b.s.size()]=g;g=;}
}
while(c.s[c.s.size()-]==) c.s.pop_back();
return c;//没打返回值……
}
bignum &operator +=(const bignum b) {
*this=*this+b;
return *this;
}
bignum &operator -=(const bignum b) {
*this=*this-b;
return *this;
}
bignum &operator *=(const bignum b) {
*this=*this*b;
return *this;
}
friend ostream& operator << (ostream &out, const bignum& x) {
for(int i=x.s.size()-;i>=;--i) {
out<<x.s[i];
}
return out;
}
friend istream& operator >> (istream &in,bignum &x) {
string str;
if(!(in>>str)) return in;
x=str;
return in;
}
}f[],g[];
int n;
void solve() {
scanf("%d",&n);
g[]=;g[]=;g[]=;f[]=;f[]=;f[]=;
siji(i,,n) {
g[i]=f[i-]*bignum()+g[i-]+g[i-]-g[i-];
f[i]=f[i-]+g[i-];
}
cout<<f[n]<<endl;
}
int main(int argc, char const *argv[])
{
#ifdef ivorysi
freopen("vans.in","r",stdin);
freopen("vans.out","w",stdout);
#else
//freopen("f1.in","r",stdin);
#endif
solve();
return ;
}

于是乎,抱着一种鬼迷心窍的态度看了看USACO这道题的Analysis

其实思路很简单,就是我英文不好理解起来费了不少时间,于是我又决定当翻译了【nocow似乎有个类似的做法】

Postal Vans
Alex Schwendner

This is a DP problem. The key to any dynamic programming problem is, of course, finding the subproblems. In this case, one noteworthy aspect of the problem is that while the grid may have up to 1000 vertical streets, it has only 4 horizontal streets. Consider a vertical cross-section cutting through the four horizontal street (and not intersecting a vertical street). There are only a few different possible ways in which our route could use these streets. Thus, we let the "state" for our DP be the distance from one end (say the right end) of the grid and the configuration of these four streets.

这是一道DP问题。当然,任何DP问题的关键是找到子问题。【真是至理名言……】在这个情况中,题中一个值得注意的方面是这个网格也许至多会有1000条垂直的街道,然而只有4条水平的街道。考虑一个垂直的横截面切过4个水平的街道(并且不与一个垂直的街道交叉)。这样我们的路径使用这些街道只会有几个不同的路径。因此,我们让DP方程的状态是和网格中一个结束点的距离(我们说的是右下角的结束点)和我们使用四个街道的形式。

【那么我解释一下“一个垂直的横截面切过4个水平的街道(并且不与一个垂直的街道交叉)”这句话】

也就是绿色那部分位于两个垂直街道之间这就是横截面,这个横截面有四个小横条(蓝色部分),下面就是对这蓝色小横条使用进行讨论

What are the possible configurations? Note that because we must connect whichever streets we use into a cycle, our path must traverse an even number of the four streets (i.e., either 2 or 4). Thus, the configuration is one of the 8 following patterns (the last two of which appear similar; read on):

-
-
 
 
-
 
-
 
-
 
 
-
 
-
-
 
 
-
 
-
 
 
-
-
-
-
-
-
-
-
-
-

哪些是可能的形式呢?注意到因为我们在这一个圈中无论使用哪个街道,我们都要经过四个街道中的偶数个(换句话,不是2就是4)【也就是我们使用不是2就是4个蓝色小横条】。因此,这个形式只能是以下八种形状的一种(最后两种出现的形状是相同的,看下面的表格)

The first 6 patterns with two lines represent all configurations in which two streets are used in the van route. The last two patterns both represent all four streets being used in the van route, but differ in the connection of the paths. In #7, the top two paths are connected and the bottom two paths are connected by the part of the van route to the right. In #8, the top and bottom paths are connected, and the middle two paths are connected by the rest of the van route (to the right).

前六个用两条横线的形状代表卡车路线用两个街道的所有形式。最后两个形状代表四个街道在卡车路线中都被使用,但是他们不同在路径的连接,在7的情况里,顶端两个路径是相连的,底端两个两个路径是相连的,通过一部分卡车去向右边的路径。在8的情况里,最顶端和最底端的路径是相连的,中间的路径通过卡车剩余的路径相连(到右边的)。

这句话理解起来有点费力,但是其实换一种说法很好理解

7:水平向左凸起一块

8:水平向右凸起一块

因为dp是不断从右向左推进,向左凸起时上两个和下两个已经通过右边的相连了,而向右凸起上下两条边已经通过最右边的边相连了,中间的还没连上

So how do we go from one state to another? To go one intersection farther to the left, we must visit all four intersections of one vertical street with the four hourzontial streets. This means, for instance, that we cannot connect two Type 1 patterns, because then we would not be visiting the bottom two intersections. Also, we cannot connect a Type 1 pattern with a Type 6 pattern, because both parts of the path would have to traverse the same section of the vertical street, and we can't visit an intersection twice. On the other hand, we could connect a Type 1 pattern with a Type 3 pattern. Lastly, we cannot connect the Type 7 and Type 8 patterns (with all four streets used) in such a way as to disconnect the route. Putting this all togeather, we have the following transition matrix:

0 0 1 0 0 0 0 1
0 0 0 0 1 0 0 0
1 0 0 1 0 1 1 0
0 0 1 0 0 0 0 0
0 1 0 0 0 0 0 0
0 0 1 0 0 0 0 1
1 0 0 0 0 1 1 0
0 0 1 0 0 0 0 1

那么我们怎么转移呢?去左边一个更远的交叉点,我们必须经过一个垂直街道穿过四个水平街道所有四个交叉点,这意味着,举个例子,我们不能把两个形状1连在一起,因为我们不能不经过两个底端的交叉点。同样,我们不能把形状1和形状6连在一起,因为这两个部分会不得不穿过一个垂直街道的相同部分,而且不能不能经过同一个交叉点两次。再者来说,我们可以把一个形状1和一个形状3连在一起。最后,我们不能连接形状7和形状8(所有4个街道都被用了)使得在一条不连通的路上。把这些都考虑了,我们有了如下的转移矩阵。【还没感受过把转移方法写成矩阵的却又没有快速幂的方式】

Above, we have a 1 in the i-th row and j-th column if we can transition to a Type i pattern from a Type j pattern. Thus, we know how to go from state to state: to calculate the numbers of routes with the 8 patterns on the left edge at a certain distance X from the right edge of the grid, apply the matrix to the numbers of routes with the 8 patterns on the left edge at distance X-1. The matrix is asymetric because patterns 7 and 8 differ in how the paths are connected to the right. Thus transitioning from a Type 7 or 8 pattern is not the same as transitioning to a Type 7 or 8 pattern.

以上,我们有一个1在i行j列表示我们可以从j状态转移到i状态。因此,我们知道怎么在状态与状态之间转移:用这个矩阵从距离右边X-1到距离右边X。【那一长句意会就好】这个矩阵是不对称的因为形状7和8不同在路径怎样连接到右边。因此从7,8转移出来和转移到7,8是不同的。

We just need initial and final conditions. To start (on the right), we can have either a Type 3 pattern or a Type 7 pattern. We need to cover all 4 intersections, so it has to use both the top and bottom streets, but we can't connect the paths in the right way for a Type 8 pattern without more room. For a Type 7 we just have two backwards "C" shaped pieces. At the end, when we get to the left side of the grid, to get our answer, we add up the number of ways to end with a Type 3 pattern or a Type 8 pattern. (Type 8 this time because we don't have room to reconnect a Type 7, but for a Type 8, we just connect the top two streets to each other and the bottom two to each other, with two "C" shaped pieces.) We then multiply by two, because we can traverse the route in two directions.

我们只需要最初和最终的状态,开始(从右)我们需要一个3或者一个7形状。我们需要覆盖所有四个交叉点,所以我们需要同时使用顶端和底端的街道,但是我们不能连接路径在右边没有任何空间的时候使用形状8,【对于形状7我们只需要两个倒C字。】(←意会!!我翻译不好!!)最后当我到方格左边的时候,为了得到我们的答案,我们加上到形状3或者形状8的值(这个时候用形状8因为我们没有空间去再连一个形状7,但是对于形状8我们只有连接两个顶端的街道并且两个底端相连,我们只需要两个正C字)我们然后乘以2,因为我们可以从两个方向走路径

这道题代码把矩阵转了圈,所以我重写一份

 /*
ID: ivorysi
LANG: C++
PROG: vans
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <set>
#include <vector>
#define siji(i,x,y) for(int i=(x);i<=(y);++i)
#define gongzi(j,x,y) for(int j=(x);j>=(y);--j)
#define xiaosiji(i,x,y) for(int i=(x);i<(y);++i)
#define sigongzi(j,x,y) for(int j=(x);j>(y);--j)
#define inf 0x5f5f5f5f
#define ivorysi
#define mo 97797977
#define hash 974711
#define base 47
#define fi first
#define se second
#define pii pair<int,int>
#define esp 1e-8
typedef long long ll;
using namespace std;
struct bignum {
vector<int> s; bignum operator =(string x) {
s.clear();
gongzi(i,x.length()-,) {
s.push_back(x[i]-'');
}
return *this;
}
bignum operator =(long long x) {
s.clear();
do {
s.push_back(x%);
x/=;
}while(x>);
return *this;
}
bignum(string x) {
*this=x;
}
bignum(long long x=0LL) {
*this=x;
}
bignum operator + (const bignum b) const {
bignum c;
c.s.clear();
for(int g=,k=;;++k) {
if(g== && k>=b.s.size() && k>=s.size()) {break;}
int x=g;
if(k<b.s.size()) x+=b.s[k];
if(k<s.size()) x+=s[k];
c.s.push_back(x%);
g=x/;
}
return c;
}
bignum operator - (const bignum b) const {
bignum c;
c.s.clear();
for(int g=,k=;;++k) {
if(g== && k>=s.size()) {break;}
int x=s[k]-g;
if(k<b.s.size()) x-=b.s[k];
if(x<) {g=;x+=;}
else g=;
c.s.push_back(x);
}
return c;
}
bignum operator * (const bignum b) const {
bignum c;
int g=,x;
c.s.clear();
siji(i,,s.size()+b.s.size()+) c.s.push_back();
for(int i=;i<s.size();++i) {
for(int j=;j<b.s.size();++j) {
x=s[i]*b.s[j]+g+c.s[i+j];
c.s[i+j]=x%;
g=x/;
}
while(g!=) {c.s[i+b.s.size()]=g;g=;}
}
while(c.s[c.s.size()-]==) c.s.pop_back();
return c;//没打返回值……
}
bignum &operator +=(const bignum b) {
*this=*this+b;
return *this;
}
bignum &operator -=(const bignum b) {
*this=*this-b;
return *this;
}
bignum &operator *=(const bignum b) {
*this=*this*b;
return *this;
}
friend ostream& operator << (ostream &out, const bignum& x) {
for(int i=x.s.size()-;i>=;--i) {
out<<x.s[i];
}
return out;
}
friend istream& operator >> (istream &in,bignum &x) {
string str;
if(!(in>>str)) return in;
x=str;
return in;
}
}f[][],ans;
int n;
int now,to;
bool martix[][]= {
{},
{,,,,,,,,},
{,,,,,,,,},
{,,,,,,,,},
{,,,,,,,,},
{,,,,,,,,},
{,,,,,,,,},
{,,,,,,,,},
{,,,,,,,,}
};
void solve() {
scanf("%d",&n);
now=;to=;
f[now][]=;f[now][]=;
siji(k,,n-) {//因为有n个垂直街道横截面只有n-1个
siji(i,,) {
f[to][i]=;
}
siji(i,,) {
siji(j,,) {
if(martix[i][j]) {
f[to][i]=f[to][i]+f[now][j];
}
}
}
siji(i,,) {
f[now][i]=f[to][i];
}
now=now^,to=to^;
}
ans=f[now][]*+f[now][]*;
if(n==) ans=;
cout<<ans<<endl;
}
int main(int argc, char const *argv[])
{
#ifdef ivorysi
freopen("vans.in","r",stdin);
freopen("vans.out","w",stdout);
#else
//freopen("f1.in","r",stdin);
#endif
solve();
return ;
}

USACO 6.1 Postal Vans(一道神奇的dp)的更多相关文章

  1. USACO 6.5 Betsy's Tour (插头dp)

    Betsy's TourDon Piele A square township has been divided up into N2 square plots (1 <= N <= 7) ...

  2. 波动数列 神奇的dp

    问题描述 观察这个数列: 1 3 0 2 -1 1 -2 ... 这个数列中后一项总是比前一项增加2或者减少3. 栋栋对这种数列很好奇,他想知道长度为 n 和为 s 而且后一项总是比前一项增加a或者减 ...

  3. [NOIP10.3模拟赛]3.w题解--神奇树形DP

    题目链接: 咕 闲扯: 这题考场上把子任务都敲满了,5个namespace,400行11k 结果爆0了哈哈,因为写了个假快读只能读入一位数,所以手测数据都过了,交上去全TLE了 把边分成三类:0. 需 ...

  4. 一道风骚的DP

    也是校赛学长出的一道题~想穿了很简单..但我还是听了学长讲才明白. 观察力有待提高. Problem D: YaoBIG's extra homeworkTime LimitMemory Limit1 ...

  5. Print Article hdu 3507 一道斜率优化DP 表示是基础题,但对我来说很难

    Print Article Time Limit: 9000/3000 MS (Java/Others)    Memory Limit: 131072/65536 K (Java/Others)To ...

  6. D. Mysterious Present (看到的一个神奇的DP,也可以说是dfs)

    D. Mysterious Present time limit per test 2 seconds memory limit per test 64 megabytes input standar ...

  7. 一道简单的dp题 --- Greenhouse Effect CodeForces - 269B

    题目链接: https://vjudge.net/problem/36696/origin 题目大意: 要求从1到m升序排列,点可以随意移动,问最少需要移动多少次, 思路: 动态规划 可以推出转移方程 ...

  8. 一道dfs和dp结合的好题 --- Longest Run on a SnowboardUVA-10285

    题目链接: https://vjudge.net/problem/19213/origin 大致题意: 一个滑雪者想知道自己在固定高度的山坡中最多能滑的距离是多少. 思路: 首先想到的就是dfs,但是 ...

  9. 一道简单树形dp

    题意:给定一棵树,从中选出一些节点,使得不成父子关系的节点对数最多.问这个最大值是多少. 思路:首先既然是给定一颗树,先要选择合适的数据结构,来保存这颗树.由于这颗树只关心根节点在哪里,所以只需要用一 ...

随机推荐

  1. MESS-配置

    Author:KillerLegend From:http://www.cnblogs.com/killerlegend/p/3824989.html Date:2014.7.4 Part A 第一部 ...

  2. NO.11天作业

    打印uid在30~40范围内的用户名.awk -F: '$3>=30 && $3<=40{print $1,$3}' /etc/passwd 打印第5-10行的行号和用户名 ...

  3. 用 Love2D 实现法线贴图的例程(到最新版本 0.10.1)

    用 Love2D 实现法线贴图的例程(到最新版本 0.10.1) 概述 一般来说, 复杂的光照模型会被用在 3D 游戏中, 以产生逼真的效果, 不过也有些开发者研究出一些代码可以在 2D 游戏中使用这 ...

  4. Windows Azure: Service Bus Relay

    Service Host: using System; using System.Collections.Generic; using System.Linq; using System.Text; ...

  5. 知识笔记:jQuery 事件对象属性小结

    使用事件自然少不了事件对象.因为不同浏览器之间事件对象的获取,以及事件对象的属性都有差异,导致我们很难跨浏览器使用事件对象.jQuery中统一了事件对象,当绑定事件处理函数时,会将jQuery格式化后 ...

  6. 可简单避免的三个 JavaScript 发布错误

    Web应用程序开发是倾向于在客户端运行所有用户逻辑和交互代码,让服务器暴露REST或者RPC接口.编译器是针对JS作为一个平台,第二版ECMAScript正是考虑到这一点在设计.客户端框架例如Back ...

  7. Python练习-函数(方法)的定义和应用

    需求:对文件进行增删改查,使用函数调用的方式完成操作 # 编辑者:闫龙 import MyFuncation; Menu = ["查询","添加"," ...

  8. 【数据库】SQL经典面试题 - 行列转换二 - 列转行

    本帖子是行转列的一个逆向操作——列转行,看下面一个面试题 面试题2: 柠檬班第30期学生要毕业了,他们的Linux.MySQL.Java成绩数据表 tb_lemon_grade_column中, 表中 ...

  9. 【译】第九篇 SQL Server代理了解作业和安全

    本篇文章是SQL Server代理系列的第九篇,详细内容请参考原文 在这一系列的上一篇,学习了如何在SQL Server代理作业步骤启动外部程序.你可以使用过时的ActiveX系统,运行批处理命令脚本 ...

  10. js 禁用右键菜单、拖拽、选中、复制

    //禁用拖拽 document.ondragstart = function () { return false; }; /** * 禁用右键菜单 */ document.oncontextmenu ...