cplex入门系列(四)--- 运输问题

一、运输问题理论介绍
在经济建设中,经常碰到大宗物资调运问题。如煤炭、钢材、木材、粮食等物资,在全国有若干生产基地,根据已有的交通网,应该如何指定调运方案,将这些物资运到各消费地点,而总运费要最小。这个问题可以用以下数学语言描述。
已知有m个生产地点A_i,i=1,2,…,m。可供应某种物资,其供应量(产量)分别为a_i,i=1,2,…,m,有n个销地B_j,j=1,2,…,n,其需要量分别为b_j,j=1,2,…,n,从A_iB_j运输单位物资的运价(单价)为c_{ij},这些数据可以汇总于产销平衡表和单位运价表中。

产地 销地 产量
1 2 … n
1 a_1
2 a_2
⋮ ⋮
m a_m
销量 b_1 b_2…b_n

产销平衡表

产地 销地
1 2 … n
1 c_{11} c_{12} … c_{1n}
2 c_{21} c_{22} … c_{2n}
⋮ ...
m c_{m1} c_{m2} … c_{mn}

单位运价表
若用x_ij 表示从A_iB_j 的运量,那么在产销平衡的条件下,要求得总运费最小得调运方案,可求解以下数学模型:
min⁡ z=∑_{i=1}^m∑_{j=1}^nc_{ij}x_{ij}
\begin{equation} \left\{ \begin{array}{r1} \sum_{i=1}^mx_{ij}=b_j, j=1,2,\dots,n \\ \sum_{i=1}^nx_{ij}=a_i, i=1,2,\dots,m\\ x_{ij}{\geq}0 \end{array} \right. \end{equation}
这就是运输问题得数学模型。它包含m×n个变量,(m+n)个约束方程。其系数矩阵得结构比较松散,且特殊。
该系数矩阵中对应于变量x_{ij} 的系数向量P_{ij},其分量中除第i个和第m+j个为1以外,其余的都为零。
P_{ij}=(0…1…0…1…0)^T=e_i+e_{m+j}
对产销平衡得运输问题,由于有以下关系存在
∑_{j=1}^nb_j=∑_{j=1}^n(∑_{i=1}^mx_{ij})=∑_{i=1}^m(∑_{j=1}^nx_{ij})=∑_{i=1}^ma_i
所以模型最多只有m+n-1个独立约束方程。即稀疏矩阵得秩≤m+n−1。由于有以上特征,所以求解运输问题时,可用比较简单得计算方法,习惯上称为表上作业法。
对产销平衡得运输问题,一定存在可行解。又因为所有变量有界,因而存在最优解。
二、cplex解决运输问题
归根到底运输问题还是一个线性规划问题,和cplex入门系列(二)中的LP问题并没有什么不同,但是由于运输问题是一个特殊的线性规划问题,即其一定存在可行解,且其结构特殊,故可以使用表上作业法进行求解。但是在cplex中,我们还是会使用求解一般线性规划的求解方式来求解问题。
2.1 案例一
假设有三个工厂Sunnyvale、Dublin、Bangkok供应四个地区Amarillo、Teaneck、Chicago和Sioux Falls的货物。各地区需求量及从各工厂到各地区送单位货物(柜)的运价表如图所示。试求出总的运费最节省的货物运输方案。

工厂/需求地区 Amarillo Teaneck Chicago Sioux Falls 产量(柜)
Sunnyvale 250 420 380 280 45
Dublin 1280 990 1440 1520 120
Bangkok 1550 1420 1660 1730 95
需求量(柜) 80 78 47 55

数学建模:这是一个产销平衡的运输问题,对应数学模型为:
min⁡ z=∑_{i=1}^3∑_{j=1}^4c_{ij}x_{ij}
\begin{equation} \left\{ \begin{array}{r1} \sum_{i=1}^3x_{ij}=b_j, j=1,2,3,4 \\ \sum_{i=1}^4x_{ij}=a_i, i=1,2,3\\ x_{ij}{\geq}0 \end{array} \right. \end{equation}
用单位运价表和产销平衡表的数据带入上述数学模型
即有
min⁡ z=250x_{11}+420x_{12}+380x_{13}+280x_{14}+1280x_{21}+990x_{22}+1440x_{23}+1520x_{24}+1550x_{31}+1420x_{32}+1660x_{33}+1730x_{34}
\begin{equation} \left\{ \begin{array}{r1} x_{11}+x_{12}+x_{13}+x_{14}=45 \\ x_{21}+x_{22}+x_{23}+x_{24}=120\\ x_{31}+x_{32}+x_{33}+x_{34}=95\\ x_{11}+x_{21}+x_{31}=80\\ x_{12}+x_{22}+x_{32}=78\\ x_{13}+x_{23}+x_{33}=47\\ x_{14}+x_{24}+x_{34}=55\\ \end{array} \right. \end{equation}
该问题是一个纯粹的线性规划问题,即可以利润cplex的LP求解器来求解。

产/销地类定义(TransportationNode )

package com.opreation.research;

/**
 * 这个用于定义运输节点
 */

public class TransportationNode {
    final String NodeName; // 节点名称
    final int quantity; // 产/销量
    final boolean isSource; // true 为产地 false 为销地
    final int id; // 下标

    public TransportationNode(String NodeName,int quantity,boolean isSource,int id) {
        this.NodeName = NodeName;
        this.quantity = quantity;
        this.isSource = isSource;
        this.id = id;
    }

    public boolean isSource() {
        return this.isSource;
    }
}

运价/距定义(TransportationRelation )

package com.opreation.research;

/**
 * @Description: 定义运价、运距
 * @Author
 * @Date 2022/8/5 10:01
 */
public class TransportationRelation {
    final int [] distance ;
    public TransportationRelation(int[] distance) {
        this.distance = distance;
    }
}

cplex数据建模

package com.opreation.research;

import ilog.concert.IloException;
import ilog.concert.IloNumExpr;
import ilog.concert.IloNumVar;
import ilog.cplex.IloCplex;

import java.util.List;

/**
 * @Description: 定义运输模型
 * @Author 
 * @Date 2022/8/5 10:13
 */
public class TransportationModel {
    IloCplex cplex ;
    double objectiveValue;
    IloNumVar x[];
    int numOfSource = 0; // 产地数量
    int numOfSink = 0; // 销地数量
    /**
     * 假设有
     */
    public void bulidModel(List<TransportationNode> transportationNodeList, TransportationRelation transportationRelation)throws IloException {
        this.cplex = new IloCplex();
        for(TransportationNode tsNode : transportationNodeList){
            if (tsNode.isSource()) {
                numOfSource++;
            }else {
                numOfSink++;
            }
        }

        System.out.println(numOfSource);
        System.out.println(numOfSink);

        CreatDecisionVariable();
        CreatObjFunc(transportationRelation);
        CreatConstraints(transportationNodeList);
        Solve(transportationNodeList);
    }
    /**
     * 构建决策变量
     */
    public void CreatDecisionVariable() throws IloException{
        x = new IloNumVar[numOfSource*numOfSink];
        for (int i = 0; i < numOfSource; i++) {
            for (int j = 0; j < numOfSink; j++) {
                x[i*numOfSink+j] = cplex.numVar(0, Double.MAX_VALUE,"x"+(i+1)+(j+1));
                int a = i*numOfSink+j;
                System.out.println("x"+a+":" + "x"+(i+1)+(j+1));
            }
        }

    }
    /**
     * 构建目标函数,最小化运输距离(运价)
     */
    public void CreatObjFunc(TransportationRelation transportationRelation) throws IloException{
        cplex.addMinimize(cplex.scalProd(x,transportationRelation.distance));

    }
    /**
     * 构建约束条件
     */
    public void CreatConstraints(List<TransportationNode> transportationNodeList) throws IloException {
        for(TransportationNode tsNode : transportationNodeList){
            IloNumExpr left = cplex.numExpr();
            if (tsNode.isSource()) {
                // 产地节点,约束条件为,流出产地的供应量和产地决策变量相等
                for(int i = 0;i<numOfSink;i++) {
                    left = cplex.sum(left,cplex.prod(1,x[tsNode.id*numOfSink+i]));
                }
                cplex.addEq(left,tsNode.quantity);
            }else {
                // 销地节点,约束条件为,流入销地的供应量与销地决策变量相等
                for(int i = 0;i<numOfSource;i++) {
                    left = cplex.sum(left,cplex.prod(1,x[tsNode.id+i*numOfSink]));
                }
                cplex.addEq(left,tsNode.quantity);
            }
        }
    }
    /**
     * 线性规划模型求解问题
     */
    public void Solve(List<TransportationNode> transportationNodeList) throws IloException {
        cplex.setOut(null);
        if (cplex.solve()) {
            cplex.exportModel("transportationModel.lp");
            objectiveValue = cplex.getObjValue();
            System.out.println("最小运费为:" + objectiveValue);
            for (int i = 0; i < numOfSource; i++) {
                for (int j = 0; j < numOfSink; j++) {
                    System.out.print(transportationNodeList.get(i).NodeName+" to "+transportationNodeList.get(numOfSource+j).NodeName+" : ");
                    System.out.println(cplex.getValue(x[i*numOfSink+j]));
                }
            }
        }else{
            System.out.println("无解");
        }
    }
}

主函数(传入参数调用求解器)

package com.opreation.research;

import ilog.concert.IloException;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * @Description: 传入参数,求解模型
 * @Author 
 * @Date 2022/8/5 10:56
 */
public class Main {
    public static void main(String[] args) throws IOException, IloException {
        // 产销数据节点
        TransportationNode transportationNode1 = new TransportationNode("Sunnyvale", 45, true, 0);
        TransportationNode transportationNode2 = new TransportationNode("Dublin", 120, true, 1);
        TransportationNode transportationNode3 = new TransportationNode("Bangkok", 95, true, 2);
        TransportationNode transportationNode4 = new TransportationNode("Amarillo", 80, false, 0);
        TransportationNode transportationNode5 = new TransportationNode("Teaneck", 78, false, 1);
        TransportationNode transportationNode6 = new TransportationNode("Chicago", 47, false, 2);
        TransportationNode transportationNode7 = new TransportationNode("Sioux Falls", 55, false, 3);
        List<TransportationNode> transportationNodeList = new ArrayList<TransportationNode>();
        transportationNodeList.add(transportationNode1);
        transportationNodeList.add(transportationNode2);
        transportationNodeList.add(transportationNode3);
        transportationNodeList.add(transportationNode4);
        transportationNodeList.add(transportationNode5);
        transportationNodeList.add(transportationNode6);
        transportationNodeList.add(transportationNode7);

        // 产销地运价/运距
        int [] dis = new int[]{250, 420, 380, 280, 1280, 990, 1440, 1520, 1550, 1420, 1660, 1730};
        TransportationRelation transportationRelation = new TransportationRelation(dis);

        TransportationModel model = new TransportationModel();
        model.bulidModel(transportationNodeList, transportationRelation);
    }
}

运行结果

最小运费为:297800.0
Sunnyvale to Amarillo : 0.0
Sunnyvale to Teaneck : 0.0
Sunnyvale to Chicago : 0.0
Sunnyvale to Sioux Falls : 45.0
Dublin to Amarillo : 42.0
Dublin to Teaneck : 78.0
Dublin to Chicago : 0.0
Dublin to Sioux Falls : 0.0
Bangkok to Amarillo : 38.0
Bangkok to Teaneck : 0.0
Bangkok to Chicago : 47.0
Bangkok to Sioux Falls : 10.0

打开transportationModel.lp可以看到模型结构,和我们构建的数学模型一致:

\ENCODING=ISO-8859-1
\Problem name: ilog.cplex

Minimize
 obj: 250 x11 + 420 x12 + 380 x13 + 280 x14 + 1280 x21 + 990 x22 + 1440 x23
      + 1520 x24 + 1550 x31 + 1420 x32 + 1660 x33 + 1730 x34
Subject To
 c1: x11 + x12 + x13 + x14  = 45
 c2: x21 + x22 + x23 + x24  = 120
 c3: x31 + x32 + x33 + x34  = 95
 c4: x11 + x21 + x31  = 80
 c5: x12 + x22 + x32  = 78
 c6: x13 + x23 + x33  = 47
 c7: x14 + x24 + x34  = 55
End

2.2 案例2
该案例为《运筹学》清华大学第四版P107 例4-1题目,具体应用场景请参考教材。
某公司经销售甲产品。它下设三个加工厂。每日的产量分别是:A_1 为7吨,A_2 为4吨,A_3 为9吨。该公司把这些产品分别运往四个销售点。各销售点每日销量为:B_1 为3吨,B_2 为6吨,B_3 为5吨,B_4 为6吨。已知从各工厂到各销售点的单位产品的运价如下表所示。问该公司应该如何调运产品,在满足各销点的需求量的前提下,使总运费为最少。

加工厂/销地 B_1 B_2 B_3 B_4
A_1 3 11 3 10
A_2 1 9 2 8
A_3 7 4 10 5
加工厂/销地 B_1 B_2 B_3 B_4 产量
A_1 7
A_2 4
A_3 9
销量 3 6 5 6

数学建模:这是一个产销平衡的运输问题,对应数学模型为:
min⁡ z=∑_{i=1}^3∑_{j=1}^4c_{ij}x_{ij}
\begin{equation} \left\{ \begin{array}{r1} \sum_{i=1}^3x_{ij}=b_j, j=1,2,3,4 \\ \sum_{i=1}^4x_{ij}=a_i, i=1,2,3\\ x_{ij}{\geq}0 \end{array} \right. \end{equation}
用单位运价表和产销平衡表的数据带入上述数学模型
即有
min⁡ z=3x_{11}+11x_{12}+3x_{13}+10x_{14}+1x_{21}+9x_{22}+2x_{23}+8x_{24}+7x_{31}+4x_{32}+10x_{33}+5x_{34}
\begin{equation} \left\{ \begin{array}{r1} x_{11}+x_{12}+x_{13}+x_{14}=7\\ x_{21}+x_{22}+x_{23}+x_{24}=4\\ x_{31}+x_{32}+x_{33}+x_{34}=9\\ x_{11}+x_{21}+x_{31}=3\\ x_{12}+x_{22}+x_{32}=6\\ x_{13}+x_{23}+x_{33}=5\\ x_{14}+x_{24}+x_{34}=6\\ \end{array} \right. \end{equation}
该问题是一个纯粹的线性规划问题,即可以利用cplex的LP求解器来求解。
产/销地类定义(TransportationNode )

package com.opreation.research;

/**
 * 这个用于定义运输节点
 */

public class TransportationNode {
    final String NodeName; // 节点名称
    final int quantity; // 产/销量
    final boolean isSource; // true 为产地 false 为销地
    final int id; // 下标

    public TransportationNode(String NodeName,int quantity,boolean isSource,int id) {
        this.NodeName = NodeName;
        this.quantity = quantity;
        this.isSource = isSource;
        this.id = id;
    }

    public boolean isSource() {
        return this.isSource;
    }
}

运价/距定义(TransportationRelation )

package com.opreation.research;

/**
 * @Description: 定义运价、运距
 * @Author
 * @Date 2022/8/5 10:01
 */
public class TransportationRelation {
    final int [] distance ;
    public TransportationRelation(int[] distance) {
        this.distance = distance;
    }
}

cplex数据建模

package com.opreation.research;

import ilog.concert.IloException;
import ilog.concert.IloNumExpr;
import ilog.concert.IloNumVar;
import ilog.cplex.IloCplex;

import java.util.List;

/**
 * @Description: 定义运输模型
 * @Author 
 * @Date 2022/8/5 10:13
 */
public class TransportationModel {
    IloCplex cplex ;
    double objectiveValue;
    IloNumVar x[];
    int numOfSource = 0; // 产地数量
    int numOfSink = 0; // 销地数量
    /**
     * 假设有
     */
    public void bulidModel(List<TransportationNode> transportationNodeList, TransportationRelation transportationRelation)throws IloException {
        this.cplex = new IloCplex();
        for(TransportationNode tsNode : transportationNodeList){
            if (tsNode.isSource()) {
                numOfSource++;
            }else {
                numOfSink++;
            }
        }

        System.out.println(numOfSource);
        System.out.println(numOfSink);

        CreatDecisionVariable();
        CreatObjFunc(transportationRelation);
        CreatConstraints(transportationNodeList);
        Solve(transportationNodeList);
    }
    /**
     * 构建决策变量
     */
    public void CreatDecisionVariable() throws IloException{
        x = new IloNumVar[numOfSource*numOfSink];
        for (int i = 0; i < numOfSource; i++) {
            for (int j = 0; j < numOfSink; j++) {
                x[i*numOfSink+j] = cplex.numVar(0, Double.MAX_VALUE,"x"+(i+1)+(j+1));
                int a = i*numOfSink+j;
                System.out.println("x"+a+":" + "x"+(i+1)+(j+1));
            }
        }

    }
    /**
     * 构建目标函数,最小化运输距离(运价)
     */
    public void CreatObjFunc(TransportationRelation transportationRelation) throws IloException{
        cplex.addMinimize(cplex.scalProd(x,transportationRelation.distance));

    }
    /**
     * 构建约束条件
     */
    public void CreatConstraints(List<TransportationNode> transportationNodeList) throws IloException {
        for(TransportationNode tsNode : transportationNodeList){
            IloNumExpr left = cplex.numExpr();
            if (tsNode.isSource()) {
                // 产地节点,约束条件为,流出产地的供应量和产地决策变量相等
                for(int i = 0;i<numOfSink;i++) {
                    left = cplex.sum(left,cplex.prod(1,x[tsNode.id*numOfSink+i]));
                }
                cplex.addEq(left,tsNode.quantity);
            }else {
                // 销地节点,约束条件为,流入销地的供应量与销地决策变量相等
                for(int i = 0;i<numOfSource;i++) {
                    left = cplex.sum(left,cplex.prod(1,x[tsNode.id+i*numOfSink]));
                }
                cplex.addEq(left,tsNode.quantity);
            }
        }
    }
    /**
     * 线性规划模型求解问题
     */
    public void Solve(List<TransportationNode> transportationNodeList) throws IloException {
        cplex.setOut(null);
        if (cplex.solve()) {
            cplex.exportModel("transportationModel.lp");
            objectiveValue = cplex.getObjValue();
            System.out.println("最小运费为:" + objectiveValue);
            for (int i = 0; i < numOfSource; i++) {
                for (int j = 0; j < numOfSink; j++) {
                    System.out.print(transportationNodeList.get(i).NodeName+" to "+transportationNodeList.get(numOfSource+j).NodeName+" : ");
                    System.out.println(cplex.getValue(x[i*numOfSink+j]));
                }
            }
        }else{
            System.out.println("无解");
        }
    }
}

主函数(传入参数调用求解器)

package com.opreation.research;

import ilog.concert.IloException;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * @Description: 传入参数,求解模型
 * @Author 
 * @Date 2022/8/5 10:56
 */
public class Main {
    public static void main(String[] args) throws IOException, IloException {
        // 产销数据节点
        TransportationNode transportationNode1 = new TransportationNode("A1", 7, true, 0);
        TransportationNode transportationNode2 = new TransportationNode("A2", 4, true, 1);
        TransportationNode transportationNode3 = new TransportationNode("A3", 9, true, 2);
        TransportationNode transportationNode4 = new TransportationNode("B1", 3, false, 0);
        TransportationNode transportationNode5 = new TransportationNode("B2", 6, false, 1);
        TransportationNode transportationNode6 = new TransportationNode("B3", 5, false, 2);
        TransportationNode transportationNode7 = new TransportationNode("B4", 6, false, 3);
        List<TransportationNode> transportationNodeList = new ArrayList<TransportationNode>();
        transportationNodeList.add(transportationNode1);
        transportationNodeList.add(transportationNode2);
        transportationNodeList.add(transportationNode3);
        transportationNodeList.add(transportationNode4);
        transportationNodeList.add(transportationNode5);
        transportationNodeList.add(transportationNode6);
        transportationNodeList.add(transportationNode7);

        // 产销地运价/运距
        int [] dis = new int[]{3, 11, 3, 10, 1, 9, 2, 8, 7, 4, 10, 5};
        TransportationRelation transportationRelation = new TransportationRelation(dis);

        TransportationModel model = new TransportationModel();
        model.bulidModel(transportationNodeList, transportationRelation);
    }
}

运行结果

最小运费为:85.0
A1 to B1 : 0.0
A1 to B2 : 0.0
A1 to B3 : 5.0
A1 to B4 : 2.0
A2 to B1 : 3.0
A2 to B2 : 0.0
A2 to B3 : 0.0
A2 to B4 : 1.0
A3 to B1 : 0.0
A3 to B2 : 6.0
A3 to B3 : 0.0
A3 to B4 : 3.0

打开transportationModel.lp可以看到模型结构,和我们构建的数学模型一致:

 obj: 3 x11 + 11 x12 + 3 x13 + 10 x14 + x21 + 9 x22 + 2 x23 + 8 x24 + 7 x31
      + 4 x32 + 10 x33 + 5 x34
Subject To
 c1: x11 + x12 + x13 + x14  = 7
 c2: x21 + x22 + x23 + x24  = 4
 c3: x31 + x32 + x33 + x34  = 9
 c4: x11 + x21 + x31  = 3
 c5: x12 + x22 + x32  = 6
 c6: x13 + x23 + x33  = 5
 c7: x14 + x24 + x34  = 6
End

可以看到结果和书上例题给出的最优解结果一致。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,657评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,889评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,057评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,509评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,562评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,443评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,251评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,129评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,561评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,779评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,902评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,621评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,220评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,838评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,971评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,025评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,843评论 2 354

推荐阅读更多精彩内容