HDU 4370 0 or 1 (01规划)【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\n",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\n",ans);
71     }
72     return 0;
73 }

2018-10-14

猜你喜欢

转载自www.cnblogs.com/00isok/p/9788724.html