• SharePoint Designer工作流的扩展


    1  SharePoint Designer工作流的迁移

    SharePoint Designer设计工作流的功能是提供给最终用户使用的,就如同用SPD定制站点页面一样,并没有提供专门的流程迁移工具。

    最终用户可以直接在生产环境中设计流程。对开发人员来说,如果在开发环境中设计好了一个比较复杂的流程,需要迁移到生产环境,必须手工操作,可以参考如下步骤。

    Step1  确保生产环境已经建好了相应的列表,并确保列表的配置跟开发环境完全一致,包括列表名称、列表的所有栏。

    Step2  SharePoint Designer打开生产环境的站点,新建一个与开发环境名称一样的流程,绑定到同名称的列表,直接保存。

    Step3  SharePoint Designer打开开发环境的站点,将工作流的所有文件复制到生产环境。

    Step4  打开所有工作流的文件,将其中所有的列表项ID替换成生产环境相应的列表项ID

    2 SharePoint Designer工作流的扩展

    SharePoint Designer提供了常用的很多条件和活动,但是这些条件和活动并不能满足所有需要。本节就来讲述SPD动作和条件的扩展。

    2.1  动作的扩展

    SPD工作流的动作是一个普通的WF活动类。WF活动类开发好以后,部署到GAC中,然后修改SPD工作流的配置文件,注册新开发的活动。

    SPD工作流配置文件路径为C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\2052\Workflow,2052表示中文语言,英文语言的配置文件路径为C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\1033\ Workflow,在这个路径下,有个WSS.ACTIONS文件,这是一个XML文件,这个文件里面配置了所有系统自带的动作和条件。

    WSS.ACTIONS在Actions节点下声明了所有的动作。SDP工作流是基于规则来定义的。在ACTIONS配置文件中,将动作类的属性与各种规则设计器关联。以下是"从用户处收集数据"动作的声明。

    <Action Name="从用户处收集数据"

         ClassName="Microsoft.SharePoint.WorkflowActions.CollectDataTask"

         Assembly="Microsoft.SharePoint.WorkflowActions, Version=12.0.0.0, Culture=

    neutral, PublicKeyToken=71e9bce111e9429c"

         AppliesTo="all"

         CreatesTask="true"

         Category="任务操作">

         <RuleDesigner Sentence=" %2 处收集 %1 (输出到 %3)">

            <FieldBind Field="Title,ContentTypeId" DesignerType="Survey" Text=

    "数据" Id="1"/>

            <FieldBind Field="AssignedTo" DesignerType="SinglePerson" Text="此用户" Id=

    "2"/>

       <FieldBind Field="TaskId" DesignerType="ParameterNames" Text="collect" Id="3"/>

         </RuleDesigner>

         <Parameters>

            <Parameter Name="__Context" Type="Microsoft.SharePoint.WorkflowActions.

    WorkflowContext, Microsoft.SharePoint.WorkflowActions"

    Direction="In" />

            <Parameter Name="ContentTypeId" Type="System.String, mscorlib" Direction=

    "In" />

            <Parameter Name="AssignedTo" Type="System.String, mscorlib" Direction=

    "In" />

            <Parameter Name="Title" Type="System.String, mscorlib" Direction="In" />

            <Parameter Name="TaskId" Type="System.Int32, mscorlib" Direction="Out" />

         </Parameters>

        </Action>

    以下是对配置文件中主要元素的说明。

    l         ClassNameAssembly分别是动作类的全名和所在的程序集。

    l         AppliesTo指定动作是应用到文档库还是列表,允许的值为listall

    l         CreatesTask指定是否需要创建任务项。

    l         Parameters节点中声明了WF活动类需要跟上下文数据进行绑定或进行设置的属性。

    在Parameter节点中,Name为WF活动类的属性名,Type为属性的类型,Direction声明属性是需要赋值的还是对外提供值,有3个可选值:In、Out、Optional,指定In时必须给属性赋值,指定Out时必须将属性跟某个工作流变量进行绑定,Optional表示可选项,指定Optional时可以不对属性进行操作。以下为3个特殊的Parameter。

    l         __Context:当Action类需要访问工作流数据时,必须声明此属性,类型是Microsoft.SharePoint.WorkflowActions.WorkflowContext

    l         __ListId:当需要访问工作流关联的列表时,需要指定此属性。

    l         __ItemID:当需要访问工作流关联的列表项时,需要指定此属性。

    以上的3个属性定义在Action类中,然后声明在配置文件中,工作流运行时引擎会自动为其赋值。

    RuleDesigner节点声明了对Action类属性的设计规则。Sentence指定了设计时显示的提示信息,其中的占位符号%与RuleDesigner中的FieldBind的ID属性配置,FieldBind定义了对某个属性的设计规则。Field为属性的名字,Text为%占位符处显示的文本,ID跟占位符%前的数字对应。DesignerType指定了对属性应用的设计器,系统支持如下的设计器。

    l         Survey:生成收集数据的表单,表单对应的内容类型ID和标题需要分别映射到两个属性中。

    l         SinglePerson:单个用户或用户组。

    l         Person:多个用户或用户组。

    l         ParameterNames:工作流变量。

    l         StringBuilder:生成可以跟当前工作流数据混合的字符串。

    l         Operator:选项,用Option指定可选值。如:

    <FieldBind Field="TaskMode" DesignerType="Operator" Text="此模式" Id="3">

              <Option Name="RequireOne" Value="RequireOne"/>

              <Option Name="RequireAll" Value="RequireAll"/>

      </FieldBind>

    l         fieldNames:列表栏,采用此设计器将工作流关联列表的某个栏和Action类的属性绑定。

    l         E-mail:邮件设计器,设计邮件的标题、内容和收件人等,需要将收件人、抄送人、标题、内容4个属性分别绑定,如:

    <RuleDesigner Sentence="电子邮件 %1">

            <FieldBind Field="To,CC,Subject,Body" Text="此电子邮件" DesignerType=

    "Email" Id="1"/>

          </RuleDesigner>

          <Parameters>      

            <Parameter Name="Body" Type="System.String, mscorlib" Direction="Optional" />

            <Parameter Name="To" Type="System.Collections.ArrayList,

    mscorlib" Direction="In" />

            <Parameter Name="CC" Type="System.Collections.ArrayList, mscorlib"

    Direction="Optional" />

            <Parameter Name="Subject" Type="System.String, mscorlib" Direction="In" />

          </Parameters>

    2.2  动作扩展示例——自定义E-mail活动

    系统默认的发送邮件动作只能发送简单的文本,很多时候,用户希望审批任务的提醒邮件中带一个链接,可以直接链接到任务操作页面。本节我开发一个可以发送任务链接的E-mail活动,这个E-mail活动具有系统发送邮件动作的所有功能,可以动态绑定收件人、指定主题等,如图1所示。唯一跟系统默认功能的区别就是:在发送出去的邮件内容中带有任务操作链接。

     

    Step1  新建一个工作流活动项目,如图2所示。将默认产生的Activity1类改名为"MailWithTaskLinkActivity"。

    新建工作流活动项目

    Step2  添加Microsoft.SharePoint.dllmicrosoft.sharepoint.WorkflowActions.dll两个程序集的引用。这两个程序集位于服务器上的C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\ISAPI目录中。

    Step3  切换到代码视图,将MailWithTaskLinkActivity的基类由SequenceActivity改为Activity

    复核活动可以从SequenceActivity类继承,但是MailWithTaskLinkActivity并不需要使用子活动(也就是不是一个复核活动),所有应该从基本的Activity类继承。

    Step4  添加对SharePoint API主要名称空间的引用,代码如下所示。

    using Microsoft.SharePoint;

    using Microsoft.SharePoint.Utilities;

    using Microsoft.SharePoint.Workflow;

    using Microsoft.SharePoint.WorkflowActions;

    namespace CodeArt.SharePoint.WorkflowActions

    {

        public class MailWithTaskLinkActivity : Activity

        {

            public MailWithTaskLinkActivity()

            {           

            }

        }

    }

    Step5  希望MailWithTaskLinkActivity部署成SharePoint Designer的动作之后,依然具有系统自带的发送邮件活动的功能可以动态绑定到收件人、指定邮件主题等,所以需要给MailWithTaskLinkActivity添加相应的属性。添加一个__Context属性,代码如下所示。

    public static DependencyProperty __ContextProperty =

              DependencyProperty.Register("__Context", typeof(WorkflowContext),

    typeof(SendMail));

            [Browsable(true), ValidationOption(ValidationOption.Required),

    DesignerSerializationVisibility(DesignerSerializationVisibility.

    Visible), Description("Context")]

            public WorkflowContext __Context

            {

                get

                {

                    return (WorkflowContext)base.GetValue(__ContextProperty);

                }

                set

                {

                    base.SetValue(__ContextProperty, value);

                }

            }

    __Context属性用来绑定到工作流的上下文,以便获取工作流运行环境的网站集、网站等信息。这个属性的名称是不能变的,后面的步骤会讲述如何在配置文件中配置这个属性。添加一个__ListId属性,代码如下所示。

    public static DependencyProperty __ListIdProperty = DependencyProperty.Register(

    "__ListId", typeof(string), typeof(MailWithTaskLinkActivity));

            [ValidationOption(ValidationOption.Required)]

            public string __ListId

            {

                get

                {

                    return (string)base.GetValue(__ListIdProperty);

                }

                set

                {

                    base.SetValue(__ListIdProperty, value);

                }

            }

    __ListId属性用来绑定到工作流的关联的列表ID。添加一个__ListItem属性,代码如下所示。

    public static DependencyProperty __ListItemProperty = DependencyProperty.Register(

    "__ListItem", typeof(int), typeof(MailWithTaskLinkActivity));

            [ValidationOption(ValidationOption.Required)]

            public int __ListItem

            {

                get

                {

                    return (int)base.GetValue(__ListItemProperty);

                }

                set

                {

                    base.SetValue(__ListItemProperty, value);

                }

            }

    __ListItem属性用来绑定到工作流的关联的列表项ID。添加一个To属性,代码如下所示。

    public static DependencyProperty ToProperty = DependencyProperty.Register("To",

    typeof(ArrayList), typeof(MailWithTaskLinkActivity));

            [ValidationOption(ValidationOption.Required)]

            public ArrayList  To

            {

                get

                {

                    return (ArrayList)base.GetValue(ToProperty);

                }

                set

                {

                    base.SetValue(ToProperty, value);

                }

            }

    To属性是一个ArrayList类型的集合,在SharePoint Designer进行设计的时候可以直接绑定到用户。同样,添加邮件的抄送(CC)、暗送(BCC)、主题(Subject)、内容(Body4个属性代码如下所示。

    public static DependencyProperty CCProperty = DependencyProperty.Register("CC",

    typeof(ArrayList), typeof(MailWithTaskLinkActivity));

            //抄送

            [ValidationOption(ValidationOption.Optional)]

            public ArrayList CC

            {

                get

                {

                    return (ArrayList)base.GetValue(CCProperty);

                }

                set

                {

                    base.SetValue(CCProperty, value);

                }

            }

            public static DependencyProperty BCCProperty = DependencyProperty.Register(

    "BCC", typeof(ArrayList), typeof(MailWithTaskLinkActivity));

            //暗送

            [ValidationOption(ValidationOption.Optional)]

            public ArrayList BCC

            {

                get

                {

                    return (ArrayList)base.GetValue(BCCProperty);

                }

                set

                {

                    base.SetValue(BCCProperty, value);

                }

            }

            public static DependencyProperty SubjectProperty = DependencyProperty.

    Register("Subject", typeof(String), typeof(MailWithTaskLinkActivity));

            //主题

            [ValidationOption(ValidationOption.Required)]

            public string Subject

            {

                get

                {

                    return (string)base.GetValue(SubjectProperty);

                }

                set

                {

                    base.SetValue(SubjectProperty, value);

                }

            }

            public static DependencyProperty BodyProperty = DependencyProperty.Register(

    "Body", typeof(String), typeof(MailWithTaskLinkActivity));

            //邮件内容

            [ValidationOption(ValidationOption.Optional)]

            public string Body

            {

                get

                {

                    return (string)base.GetValue(BodyProperty);

                }

                set

                {

                    base.SetValue(BodyProperty, value);

                }

            }

    Step6  上一步把MailWithTaskLinkActivity需要的所有属性都添加好了,下面来添加发送邮件的处理代码。重载Execute方法,代码如下所示。

            protected override ActivityExecutionStatus Execute(

    ActivityExecutionContext provider)

            {

                //获取到工作流服务

                ISharePointService service = (ISharePointService)provider.GetService(

    typeof(ISharePointService));

                if (service == null)

                {

                    throw new InvalidOperationException();

                }

                try

                {

                    //获取到列表

                    SPList list = __Context.Web.Lists[new Guid(__ListId)];

                    //获取到列表项

                    SPListItem item = list.GetItemById(Convert.ToInt32(__ListItem));

                    //计算任务查看URL

                    string url = this.__Context.Web.Url +

                    "_layouts/codeArt/SPTaskRedirect.aspx?ListId=" + item.ParentList.ID +

    "&ItemId=" + item.ID;               

                    //发送邮件参数

                    StringDictionary headers = new StringDictionary();

                    headers["to"] = this.ParseSendTo(this.To);

                    headers["subject"] = this.Subject;

                    if (null != this.CC)

                    {

                        headers["cc"] = this.ParseSendTo(this.CC);

                    }

                    if (null != this.BCC)

                    {

                        headers["bcc"] = this.ParseSendTo(this.BCC);

                    }

                    string body = null;

                    if (null != this.Body)

                    {

                        Activity parent = provider.Activity;

                        while (parent.Parent != null)

                        {

                            parent = parent.Parent;

                        }

                        //处理邮件内容中的属性绑定,Helper是系统自带的类

                        body = Helper.ProcessStringField(this.Body, parent, this.__Context);

                    }

                    body += "<br/><a href='" + url + "'><b>点击此处查看或处理任务</b></a>";

                    //发送邮件

                    service.SendEmail(base.WorkflowInstanceId, false, headers, body);

                    return ActivityExecutionStatus.Closed;

                }

                catch (Exception ex)

                {

                    //将异常信息记录到日志列表

                    service.LogToHistoryList(base.WorkflowInstanceId,

    SPWorkflowHistoryEventType.WorkflowError,

    __Context.Web.CurrentUser.ID, TimeSpan.MinValue,

    "MailWithTaskLinkActivity Error",

                        ex.Message + ex.StackTrace, "");

                }

                return ActivityExecutionStatus.Faulting;

            }

    发送邮件活动一般放置在从用户处收集数据活动之前,那么在发送邮件活动执行的时候审批任务是没有创建的,这时候无法获取任务的路径或ID,所以我们采用一个中转页面(SPTaskRedirect.aspx),将列表项的ID传给这个页面,SPTaskRedirect.aspx负责转向任务操作页面。ParseSendTo方法用来将存放邮件地址和账号的ArraryList转换成合法的收件人格式,这个函数反编译自系统自带的MailActivity

    Step7  编写SPTaskRedirect.aspx页面代码,并将其复制到12\TEMPLATE\LAYOUTS\CodeArt目录下。

    <%@ Page Language="C#" %>

    <%@ Assembly Name="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral,

    PublicKeyToken=71e9bce111e9429c" %>

    <%@ Import Namespace="Microsoft.SharePoint" %>

    <script runat="server">   

    void Page_Load(object sender , EventArgs e)

    {

        //获取到列表

        SPList list = SPContext.Current.Web.Lists[ new Guid(Request.QueryString

    ["ListId"]) ];

        //获取到列表项

        SPListItem item = list.GetItemById(Request.QueryString["ItemID"])

        //当前用户ID

        int useId = SPContext.Current.Web.CurrentUser.ID;  

        Microsoft.SharePoint.Workflow.SPWorkflowTask currentTask = null;

        //查找当前用户的任务

        foreach (Microsoft.SharePoint.Workflow.SPWorkflowTask task in item.Tasks)

        {

            if ("" + task["PercentComplete"] == "1")

                continue;

            string assignedTo = "" + task["AssignedTo"];

            if (assignedTo == "")

                continue;

            SPFieldUserValue user = new SPFieldUserValue(SPContext.Current.Web,

    assignedTo);

            if (useId == user.LookupId)

            {

                currentTask = task;

                break;

            }

        }

        if (currentTask == null)

        {

            Response.Write("任务不存在或已被删除。");

            return;

        }

        //转向任务编辑页面

        Response.Redirect(currentTask.ContentType.EditFormUrl + "?List=" +

    currentTask.ParentList.ID + "&ID=" + currentTask.ID);

    }

    </script>

    Step8  将项目进行签名,利用reflector找到程序集的全名,代码如下所示。

    CodeArt.SharePoint.WorkflowActions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=8d0e2047bbdccb4d

    Step9  创建一个XML文件,改名为CodeArt.Actions,并将其复制到12\TEMPLATE\2052\ Workflow目录下。

    <?xml version="1.0" encoding="utf-8" ?>

    <WorkflowInfo>

    <Actions Sequential="then" Parallel="and">

    <Action Name="CodeArt_发送电子邮件"

        ClassName="CodeArt.SharePoint.WorkflowActions.MailWithTaskLinkActivity"

          Assembly="CodeArt.SharePoint.WorkflowActions, Version=1.0.0.0, Culture=

    neutral, PublicKeyToken=8d0e2047bbdccb4d"

        Category="SmartForm"

       AppliesTo="all"

         UsesCurrentItem="true"

         >

       <RuleDesigner Sentence="电子邮件 %1">

            <FieldBind Field="To,CC,Subject,Body" Text="此电子邮件" DesignerType=

    "Email" Id="1"/>

       </RuleDesigner>

       <Parameters>

            <Parameter Name="__Context" Type="Microsoft.SharePoint.WorkflowActions.

    WorkflowContext, Microsoft.SharePoint.WorkflowActions" Direction=

    "In"/>

            <Parameter Name="__ListId" Type="System.String, mscorlib" Direction=

    "In" />

            <Parameter Name="__ListItem" Type="System.Int32, mscorlib" Direction=

    "In" />

            <Parameter Name="Body" Type="System.String, mscorlib" Direction=

    "Optional" />

            <Parameter Name="To" Type="System.Collections.ArrayList, mscorlib"

    Direction="In" />

            <Parameter Name="CC" Type="System.Collections.ArrayList, mscorlib"

    Direction="Optional" />

            <Parameter Name="Subject" Type="System.String, mscorlib" Direction=

    "In" />

       </Parameters>

    </Action>

    </Actions>

    </WorkflowInfo>

    RuleDesigner节点配置了采用E-mail设计器设计MailWithTaskLinkActivity 类的ToCCSubjectBody 4个属性。在Parameters节点中,指定了相应属性的输入/输出类型。

    Step10 将程序集(CodeArt.SharePoint.WorkflowActions.dll)部署到GAC中,配置文件(CodeArt.Actions)添加到12\TEMPLATE\2052\Workflow目录中后,还需要修改应用程序的web.config文件,在authorizedType节点下添加如下配置:

    <authorizedType Assembly="CodeArt.SharePoint.WorkflowActions,Version=1.0.0.0,

    Culture=neutral, PublicKeyToken=8d0e2047bbdccb4d" Namespace=

    "CodeArt.SharePoint.WorkflowActions" TypeName="*" Authorized="True" />

    配置完成后,在SharePoint Designer中设计流程的时候,就可以选择这个自定义的发送邮件动作了,如图3所示。

    3 选择自己开发的发送邮件动作

    2.3  条件的扩展

    SPD的条件是一个普通的类静态函数,系统默认的条件同样配置在WSS.ACTIONS文件中。以下是"标题域包含关键字"条件的配置。

       <Condition Name="标题域包含关键字"

                    FunctionName="WordsInTitle"

                    ClassName="Microsoft.SharePoint.WorkflowActions.Helper"

                    Assembly="Microsoft.SharePoint.WorkflowActions, Version=12.0.0.0,

    Culture=neutral, PublicKeyToken=71e9bce111e9429c"

                    AppliesTo ="list"

                    UsesCurrentItem="true">

         <RuleDesigner Sentence="标题域包含 %1">

            <FieldBind Id="1" Field="_1_" Text="关键字"/>

         </RuleDesigner>

         <Parameters>

            <Parameter Name="_1_" Type="System.String, mscorlib" Direction="In" />

         </Parameters>

       </Condition>

    与动作类的配置很类似,ClassNameAssembly分别是条件方法所在的类的全名和类所在的程序集,FunctionName是静态函数的函数名。实现条件的静态函数必须采用如下签名。

    public static bool SomeFunction(WorkflowContext context, string listId,

    int listItem, string var1,string var2)

    {

     //…

    }

    其中contextlistIdlistItem分别是工作流上下文数据、当前列表的ID和当前列表项的ID,这3个参数由工作流引擎来赋值。第4个以后的参数是额外的特殊参数,需要声明到配置文件的Parameters节点中。

    Parameter节点的Name属性表示参数的顺序号,因为前3个参数是系统必需的,所以从第4个参数开始,顺序号为1,并且要用_1_的形式。

    RuleDesigner中的FieldBind定义了参数的设计器,通过Field跟Parameter中声明的参数对应。FieldBind支持的其他属性(如DesignerType)和动作的配置完全一样。

    2.4  条件扩展示例——自定义E-mail活动

    采用SharePoint Designer设计审批流程的时候,很多情况下需要按照提交人的将审批任务分配给不同的审批人,比如,普通员工的请假需要部门经理审批,部门经理的请假需要公司总监来审批。本节我们就来开发这样一个条件,可以用于判断流程的启动用户是否属于某个网站组。

    Step1  创建条件方法。新建一个类WorkflowConditions,添加一个静态函数CompareOriginatorGroup,完整的代码如下所示。

    namespace CodeArt.SharePoint.WorkflowActions

    {

        ///<summary>

        ///条件类

        ///</summary>

        public class WorkflowConditions

        {

            ///<summary>

            ///比较流程发起人所属组

            ///</summary>

            ///<param name="context">工作流上下文</param>

            ///<param name="listId">关联列表ID</param>

            ///<param name="itemId">关联列表项ID</param>

            ///<param name="groupName">组名</param>

            ///<returns></returns>

        public static bool CompareOriginatorGroup(WorkflowContext context, string listId,

          int itemId,

                string groupName )

            {

                //SPList list = context.Web.Lists[new Guid(listId)]; //获取关联的列表

                //获取关联的列表项

                //SPListItem item = list.GetItemById(Convert.ToInt32(itemId));

                //初始化工作的参数

                SPWorkflowActivationProperties initProp = new SPWorkflowActivationProperties();

                context.Initialize(initProp);

                SPGroup group = context.Web.SiteGroups[groupName];

                if (group == null)

                    throw new Exception(String.Format("[{0}]不存在或被删除", groupName));

                string userLoginName = initProp.Originator.ToLower(); //流程启动人

                //判断流程启动人是否属于组

                foreach (SPUser user in group.Users)

                {

                    if (user.LoginName.ToLower() == userLoginName) //排除系统管理员

                        return true;

                }

                return false;

            }

        }

    }

    Step2  编写配置文件。在CodeArt.ACTIONS文件中添加如下配置。

    <Conditions And="" Or="" Not="" When="如果" Else="否则">

       <Condition Name="CodeArt_比较提交人所属组"

            FunctionName="CompareOriginatorGroup"

            ClassName="CodeArt.SharePoint.WorkflowActions. WorkflowConditions"

               Assembly="CodeArt.SharePoint.WorkflowActions, Version=1.0.0.0,

    Culture=neutral, PublicKeyToken=8d0e2047bbdccb4d"

            AppliesTo="all"   

            UsesCurrentItem="true">

            <RuleDesigner Sentence="若提交人属于 %1 网站组">

                 <FieldBind  Id="1" Field="_1_" DesignerType="TextArea" Text=""/>          

            </RuleDesigner>

            <Parameters>

                 <Parameter Name="_1_" Type="System.String, mscorlib" Direction="In" />               

            </Parameters>

       </Condition>

    </Conditions>

    以上配置制定了条件名称,以及条件对象的方法。指定了采用TextArea(文本框)设计第一个额外参数(groupName)。将配置文件部署到12\TEMPLATE\2052\Workflow中,程序集部署到GAC后,在SharePoint Designer中就可以使用这个自定义的"比较提交人所属组"条件了,如图4所示。

    自定义的条件

     

  • 相关阅读:
    日志服务器 03-部署日志服务器(网络设备部分)
    日志服务器 02-部署日志服务器(主机部分)
    日志服务器 01-Linux日志系统syslog
    nyoj--523--亡命逃窜(BFS水题)
    hdoj--3488--Tour(KM)
    hdoj--1533--Going Home(KM)
    hdoj--2255--奔小康赚大钱(KM算法模板)
    LightOJ--1152--Hiding Gold(二分图奇偶建图)(好题)
    hdoj--5093--Battle ships(二分图经典建图)
    LightOJ--1149--Factors and Multiples(二分图好题)
  • 原文地址:https://www.cnblogs.com/KimhillZhang/p/1529726.html
Copyright © 2020-2023  润新知