• HDU 4370 0 or 1 (01规划)【Dijkstra】||【spfa】


    <题目链接>

    题目大意:

    一个n*n的01矩阵,满足以下条件

    1.X12+X13+...X1n=1
    2.X1n+X2n+...Xn-1n=1
    3.for each i (1<i<n), satisfies ∑Xki (1<=k<=n)=∑Xij (1<=j<=n).

    另给出一个矩阵C,求∑Cij*Xij(1<=i,j<=n)的最小值。

    解题分析:

    显然,题目给的是一个0/1规划模型。

    解题的关键在于如何看出这个模型的本质。

    3个条件明显在刻画未知数之间的关系,从图论的角度思考问题,容易得到下面3个结论:

    1.X12+X13+...X1n=1 于是1号节点的出度为1

    2..X1n+X2n+...Xn-1n=1 于是n号节点的入度为1

    3.∑Xki =∑Xij 于是2~n-1号节点的入度必须等于出度

    于是3个条件等价于一条从1号节点到n号节点的路径,故Xij=1表示需要经过边(i,j),代价为Cij。Xij=0表示不经过边(i,j)。注意到Cij非负且题目要求总代价最小,因此最优答案的路径一定可以对应一条简单路径。

    最终,我们直接读入边权的邻接矩阵,跑一次1到n的最短路即可,记最短路为path。

    以上情况设为A

    非常非常非常非常非常非常非常非常抱歉,简单路径只是充分条件,但不必要。(对造成困扰的队伍深表歉意)

    漏了如下的情况B:

    从1出发,走一个环(至少经过1个点,即不能是自环),回到1;从n出发,走一个环(同理),回到n。

    容易验证,这是符合题目条件的。且A || B为该题要求的充要条件。

    由于边权非负,于是两个环对应着两个简单环。

    因此我们可以从1出发,找一个最小花费环,记代价为c1,再从n出发,找一个最小花费环,记代价为c2。(只需在最短路算法更新权值时多加一条记录即可:if(i==S) cir=min(cir,dis[u]+g[u][i]))

    故最终答案为min(path,c1+c2)。

    Dijkstra代码:

     1 #include <cstdio>
     2 #include <cstring>
     3 #include <algorithm>
     4 using namespace std;
     5 
     6 const int N =305;
     7 #define INF 0x3f3f3f3f
     8 int n;
     9 int cost[N][N];
    10 int dis[N];
    11 bool vis[N];
    12 
    13 void dij(int s){
    14     memset(vis,false,sizeof(vis));
    15     for(int i=1;i<=n;i++){         //初始化
    16         if(i==s)dis[i]=INF;    //将s的dis值初始化为INF,这样就可以从其它点开始松弛最短路,从而得到s的闭环长度,因为其它点cur的dis初始化为cost[s][cur]。所以用s到cur的距离,再加上cur到s的最短距离,确实是s的最小非自环
    17         else if(cost[s][i]!=INF)dis[i]=cost[s][i];
    18         else dis[i]=INF;
    19     }
    20     for(int k=1;k<=n;k++){
    21         int mn=INF,cur;
    22         for(int i=1;i<=n;i++){
    23             if(!vis[i]&&dis[i]<mn){  
    24                 mn=dis[i];
    25                 cur=i;
    26             }
    27         }
    28         vis[cur]=true;
    29         for(int i=1;i<=n;i++){
    30             if(!vis[i]&&dis[i]>dis[cur]+cost[cur][i]){
    31                 dis[i]=dis[cur]+cost[cur][i];
    32             }
    33         }
    34     }
    35 }
    36 
    37 int main(){
    38     while(scanf("%d",&n)!=EOF){
    39         for(int i=1;i<=n;i++){
    40             for(int j=1;j<=n;j++){
    41                 scanf("%d",&cost[i][j]);
    42             }
    43         }
    44         dij(1);
    45         int loop1,loopn,dist;   //loop1为1的最短非自环,loop2为n的最短非自环
    46         loop1=dis[1],dist=dis[n];   
    47         dij(n);
    48         loopn=dis[n];
    49         int ans=min(dist,loop1+loopn);
    50         printf("%d
    ",ans);
    51     }
    52     return 0;
    53 }

    spfa代码:

     1 #include<stdio.h>
     2 #include<iostream>
     3 #include<string.h>
     4 #include<algorithm>
     5 using namespace std;
     6 
     7 const int INF=0x3f3f3f3f;
     8 const int MAXN=330;
     9 int cost[MAXN][MAXN];//保存路径长度的邻接矩阵
    10 int dist[MAXN];
    11 int que[MAXN];//注意队列的循环利用,建成循环队列
    12 bool vis[MAXN];//是否在队列中标记
    13 
    14 void SPFA(int start,int n)
    15 {
    16     int front=0,rear=0;
    17     for(int v=1;v<=n;v++)//初始化
    18     {
    19         if(v==start)//由于要找start的闭环,所以dist[start]设为INF,且不入队
    20         {
    21             dist[v]=INF;
    22             vis[v]=false;
    23         }
    24         else if(cost[start][v]!=INF)
    25         {
    26             dist[v]=cost[start][v];
    27             que[rear++]=v;
    28             vis[v]=true;
    29         }
    30         else//即dist[start][v]==INF情况,对本题没有这种情况
    31         {
    32             dist[v]=INF;
    33             vis[v]=false;
    34         }
    35     }
    36 
    37     while(front!=rear)//注意这个条件是不等,因为是循环队列
    38     {
    39         int u=que[front++];
    40         for(int v=1;v<=n;v++)
    41         {
    42             if(dist[v]>dist[u]+cost[u][v])
    43             {
    44                 dist[v]=dist[u]+cost[u][v];
    45                 if(!vis[v])//不在队列
    46                 {
    47                     vis[v]=true;
    48                     que[rear++]=v;
    49                     if(rear>=MAXN) rear=0;//循环队列
    50                 }
    51             }
    52         }
    53         vis[u]=false;
    54         if(front>=MAXN)front=0;
    55     }
    56 
    57 }
    58 int main(){
    59     int n;
    60     while(scanf("%d",&n)!=EOF){
    61         for(int i=1;i<=n;i++)
    62           for(int j=1;j<=n;j++)
    63             scanf("%d",&cost[i][j]);
    64         SPFA(1,n);
    65         int ans=dist[n];//1到n的最短路
    66         int loop1=dist[1];//1的闭环长度
    67         SPFA(n,n);
    68         int loopn=dist[n];//n的闭环长度
    69         ans=min(ans,loop1+loopn);
    70         printf("%d
    ",ans);
    71     }
    72     return 0;
    73 }

    2018-10-14

  • 相关阅读:
    JVM(六)——垃圾回收算法
    JVM(五)——执行引擎、String
    JVM(四)——方法区
    JVM(三)——堆
    JVM(二)——虚拟机栈
    JVM(一)——概述和类加载子系统
    Java EE入门(二十二)——Linux和Nginx
    操作系统(六)——磁盘和IO
    【03】RNN
    【02】时间复杂度
  • 原文地址:https://www.cnblogs.com/00isok/p/9788724.html
Copyright © 2020-2023  润新知