它们其实都是“图”


二分图判定

  • 问题描述:给定一个具有n个顶点的图,要对图上每个顶点染色,并且要使相邻的顶点颜色不同,问是否能最多用2种颜色进行染色。题目保证没有重边和自环。
  • 限制条件:1≤n≤1000
  • 分析:
    科普:把相邻点染成不同颜色的问题叫做图着色问题。对图进行染色所需要的最小颜色称为最小着色数。最小着色数是2的图称作二分图。
    如果只用2种颜色,那么确定一个顶点的颜色之后,和它相邻的顶点的颜色也就确定了。因此可以用dfs进行遍历,选择任意一个顶点出发,依次确定相邻顶点的颜色,就可以判断是否可以被2种颜色染色了。
  • 代码:
     1 #include <cstdio>
     2 #include <vector>
     3 using namespace std;
     4 
     5 const int MAX_V=10000;
     6 vector<int> G[MAX_V];//邻接表存图 
     7 int V,E;
     8 int color[MAX_V];//顶点的颜色1或-1 
     9 
    10 bool dfs(int, int);//把顶点染成1或-1 
    11 
    12 int main()
    13 {
    14     scanf("%d %d", &V, &E);
    15     for (int i=0; i<E; i++)
    16     {
    17         int s,t;
    18         scanf("%d %d", &s, &t);
    19         G[s].push_back(t);
    20         G[t].push_back(s);
    21     }
    22     for (int i=0; i<V; i++)
    23     {
    24         if (color[i]==0)
    25         {
    26             if (!dfs(i,1))//如果顶点还没被染色,则染成1 
    27             {
    28                 printf("No\n");
    29                 return 0;
    30             }
    31         }
    32     }
    33     printf("Yes\n");
    34     return 0;
    35 }
    36 
    37 bool dfs(int v, int c)
    38 {
    39     color[v]=c;//把顶点v染成颜色c 
    40     for (int i=0; i<G[v].size(); i++)
    41     {
    42         //如果相邻顶点同色,则返回false
    43         if (color[G[v][i]]==c) return false; 
    44         //如果相邻顶点还没被染色,则染成-c 
    45         if (color[G[v][i]]==0 && !dfs(G[v][i],-c)) return false;// 
    46     }
    47     //如果所有顶点都染过色了,则返回true 
    48     return true;
    49 }
    二分图判定

 最短路问题


单源最短路问题1(Bellman-Ford算法)

  • 记从起点s出发到顶点i的最短距离为d[i],则下述等式成立:d[i]=min{d[j]+(从j到i的边的权值)|e(j,i)∈E}
  • Bellman-Ford算法:记当前到顶点i的最短路长度为d[i],并设初值d[s]=0,d[i]=INF,不断使用上面这条递推关系式更新d的值,就可以算出新的d。只要图中不存在负圈,这样的更新操作就是有限的。结束之后的d就是所求的最短距离了,这部分代码如下:
     1 #include <cstdio>
     2 #include <vector>
     3 using namespace std;
     4 
     5 struct edge
     6 {
     7     int from, to, cost;
     8 };
     9 const int INF=0x3ffffff;
    10 const int MAX_E=10000;
    11 const int MAX_V=10000;
    12 edge es[MAX_E];
    13 int d[MAX_V];
    14 int V,E,s;
    15 
    16 int main()
    17 {
    18     scanf("%d %d %d", &V, &E, &s);
    19     for (int i=0; i<E; i++)
    20     {
    21         scanf("%d %d %d", &es[i].from, &es[i].to, &es[i].cost);
    22     }
    23     for (int i=0; i<V; i++) d[i]=INF;
    24     d[s]=0;
    25     while (true)
    26     {
    27         bool update=false;
    28         for (int i=0; i<E; i++)
    29         {
    30             edge e=es[i];
    31             if (d[e.from]!=INF && d[e.from]+e.cost<d[e.to])
    32             {
    33                 d[e.to]=d[e.from]+e.cost;
    34                 update=true;
    35             }
    36         }
    37         if (!update) break;
    38     }
    39     for (int i=0; i<V; i++) printf("%d ", d[i]);
    40 }

     如果在图中不存在从s可达的负圈,那么最短路不会经过同一个顶点两次(也就是说最多通过|V|-1条边),while(true)的循环最多执行|V|-1次,因此,复杂度是O(|V|*|E|)。反之,如果存在从s可达的负圈,那么在第|V|次循环中也会更新d的值,因此也可以用这个性质来检查负圈。如果一开始对所有的顶点都把d[i]初始化为0,那么可以检查出所有的负圈,这部分代码如下:

     1 #include <cstdio>
     2 #include <vector>
     3 #include <cstring>
     4 
     5 using namespace std;
     6 
     7 struct edge
     8 {
     9     int from, to, cost;
    10 };
    11 const int INF=0x3ffffff;
    12 const int MAX_E=10000;
    13 const int MAX_V=10000;
    14 edge es[MAX_E];
    15 int d[MAX_V];
    16 int V,E,s;
    17 bool find_negative_loop();
    18 
    19 int main()
    20 {
    21     scanf("%d %d %d", &V, &E, &s);
    22     for (int i=0; i<E; i++)
    23     {
    24         scanf("%d %d %d", &es[i].from, &es[i].to, &es[i].cost);
    25     }
    26     if (find_negative_loop()) printf("Exist negative loop!");
    27     else printf("No negative loop.");
    28 }
    29 
    30 bool find_negative_loop()
    31 {
    32     memset(d,0,sizeof(d));
    33     for (int i=0; i<V; i++)
    34     {
    35         for (int j=0; j<E; j++)
    36         {
    37             edge e=es[j];
    38             if (d[e.to]>d[e.from]+e.cost) 
    39             {
    40                 d[e.to]=d[e.from]+e.cost;
    41                 if (i==V-1) return true;
    42             }
    43         }
    44     }
    45     return false;
    46 }
  • Bellman-Ford+队列优化即为SPFA:
     1 #include <cstdio>
     2 #include <cstring>
     3 #include <queue>
     4 
     5 using namespace std;
     6 
     7 struct edge
     8 {
     9     int to;
    10     int cost;
    11 };
    12 const int INF=0x3f3f3f3f;
    13 const int MAX_V=100000;
    14 const int MAX_E=200000;
    15 int d[MAX_V];
    16 int inque[MAX_V];
    17 vector<edge> G[MAX_V];
    18 int V,E,s;
    19 void spfa(int);
    20 queue<int> que;
    21 
    22 int main()
    23 {
    24     scanf("%d %d %d", &V, &E, &s);
    25     --s;
    26     for (int i=0; i<E; i++)
    27     {
    28         edge e;
    29         int s,t,c;
    30         scanf("%d %d %d", &s, &t, &c);
    31         e.to=t-1;e.cost=c;
    32         G[s-1].push_back(e);
    33     }
    34     spfa(s);
    35     for (int i=0; i<V; i++) 
    36     {
    37         if (d[i]!=INF) printf("%d ", d[i]);
    38         else printf("%d ", 2147483647);
    39     }
    40 }
    41 
    42 void spfa(int s)
    43 {
    44     memset(d,0x3f,sizeof(d));//0x3f3f3f3f是一个非常棒的无穷大的选择! 
    45     d[s]=0;
    46     while(!que.empty()) que.pop();
    47     que.push(s);
    48     inque[s]=true;
    49     while (!que.empty())
    50     {
    51         int u=que.front();
    52         que.pop();
    53         inque[u]=false;
    54         for (int i=0; i<G[u].size(); i++)
    55             if (d[u]+G[u][i].cost<d[G[u][i].to])
    56             {
    57                 d[G[u][i].to]=d[u]+G[u][i].cost;
    58                 if (!inque[G[u][i].to])
    59                 {
    60                     inque[G[u][i].to]=true;
    61                     que.push(G[u][i].to);
    62                 }
    63             }
    64     }
    65 }

 单源最短路问题2(Dijkstra算法)

  •  考虑没有负边的情况,在Bellman-Ford算法中,如果d[i]还不是最短距离的话,那么即使进行d[j]=d[i]+(从i到j的边的权值)的更行,d[j]也不会变成最短距离,而且即使d[i]没有变化,每一次循环也要检查一遍从i出发的所有边,这显然是在浪费时间,因此可以对算法做如下的修改:
    ①找到最短距离已经确定的顶点,从它出发更新相邻顶点的最短距离
    ②此后不需要再关心①中的“最短距离已经确定的顶点”
    在①和②中提到的“最短距离已经确定的顶点”要怎么得到是问题的关键。在最开始,只有起点的最短距离是确定的。而在尚未使用过的顶点中,距离d[i]最小的顶点就是最短距离已经确定的顶点,这是由于不存在负边,所有d[i]不会在之后的更新中变小。这就是Dijkstra算法,代码如下:
     1 #include <cstdio>
     2 #include <vector>
     3 #include <algorithm>
     4 
     5 using namespace std;
     6 
     7 struct edge
     8 {
     9     int to;
    10     int cost;
    11 };
    12 
    13 const int MAX_V=100000;
    14 const int MAX_E=200000;
    15 const int INF=0x3f3f3f3f;
    16 vector<edge> G[MAX_V];
    17 int V,E,S;
    18 int d[MAX_V];
    19 bool used[MAX_V];
    20 
    21 void dijkstra(int);
    22 
    23 int main()
    24 {
    25     scanf("%d %d %d", &V, &E, &S);
    26     --S;
    27     for (int i=0; i<E; i++)
    28     {
    29         int s,t,c;
    30         scanf("%d %d %d", &s, &t, &c);
    31         edge e;
    32         e.to=t-1;e.cost=c;
    33         G[s-1].push_back(e);
    34     }
    35     dijkstra(S);
    36     for (int i=0; i<V; i++)
    37         if (d[i]!=INF) printf("%d ", d[i]);
    38         else printf("%d ", 2147483647);
    39 }
    40 
    41 void dijkstra(int s)
    42 {
    43     fill(d, d+V, INF);
    44     fill(used, used+V, false);
    45     d[s]=0;
    46     while (true)
    47     {
    48         int v=-1;
    49         for (int u=0; u<V; u++) 
    50             if (!used[u] && (v==-1 || d[u]<d[v])) v=u; 
    51         if (v==-1) break;
    52         used[v]=true;
    53         for (int i=0; i<G[v].size(); i++)
    54             if (d[G[v][i].to]>d[v]+G[v][i].cost) 
    55                 d[G[v][i].to]=d[v]+G[v][i].cost;
    56     }
    57 }

     Dijkstra算法的复杂度是O(|V|2),大部分的时间花在了查找下一个使用的顶点上,因此需要使用合适的数据结构对其进行优化

  • 需要优化的是数值的插入(更新)和取出最小值两个操作,因此使用堆就可以了。把每个顶点当前的最短距离用堆维护,在更新最短距离时,把对应的元素往根的方向移动以满足堆的性质。而每次从堆中取出的最小值就是下一次要使用的顶点。堆中元素共有O(|V|)个,更新和取出数值的操作有O(|E|)次,因此整个算法的复杂度是O(|E|log|V|)
    下面是使用STL的priority_queue的实现:
     1 #include <cstdio>
     2 #include <vector>
     3 #include <algorithm>
     4 #include <functional>
     5 #include <queue>
     6 
     7 using namespace std;
     8 
     9 struct edge
    10 {
    11     int to;
    12     int cost;
    13 };
    14 
    15 typedef pair<int, int> P;
    16 
    17 const int MAX_V=100000;
    18 const int MAX_E=200000;
    19 const int INF=0x3f3f3f3f;
    20 vector<edge> G[MAX_V];
    21 int V,E,S;
    22 int d[MAX_V];
    23 
    24 void dijkstra(int);
    25 
    26 int main()
    27 {
    28     scanf("%d %d %d", &V, &E, &S);
    29     --S;
    30     for (int i=0; i<E; i++)
    31     {
    32         int s,t,c;
    33         scanf("%d %d %d", &s, &t, &c);
    34         edge e;
    35         e.to=t-1;e.cost=c;
    36         G[s-1].push_back(e);
    37     }
    38     dijkstra(S);
    39     for (int i=0; i<V; i++)
    40         if (d[i]!=INF) printf("%d ", d[i]);
    41         else printf("%d ", 2147483647);
    42 }
    43 
    44 void dijkstra(int s)
    45 {
    46     fill(d, d+V, INF);
    47     d[s]=0;
    48     priority_queue<P, vector<P>, greater<P> > que;
    49     que.push(P(0,s));
    50     while (!que.empty())
    51     {
    52         P p=que.top();que.pop();
    53         int v=p.second;
    54         if (d[v]<p.first) continue;
    55         for (int i=0; i<G[v].size(); i++)
    56         {
    57             edge e=G[v][i];
    58             if (d[e.to]>d[v]+e.cost)
    59             {
    60                 d[e.to]=d[v]+e.cost;
    61                 que.push(P(d[e.to], e.to));
    62             }
    63         }
    64     }
    65 }

任意两点间的最短路问题(Floyd-Warshall算法)
求解所有两点间的最短路的问题。
用DP求解:只使用顶点0~k和i,j的情况下,记i到j的最短路长度为d[k][i][j]。
当k=-1时,认为只使用i和j,所以d[-1][i][[j]=cost[i][j]。
接下来把只使用顶点0~k的问题规约到只使用0~k-1的问题上:只使用0~k时,我们分i到j的最短路正好经过顶点k一次和完全不经过顶点k两种情况来讨论。①不经过顶点k的情况下,d[k][i][j]=d[k-1][i][j],②通过顶点k的情况下,d[k][i][j]=d[k-1][i][k]+d[k-1][k][j]。
合起来,就得到了d[k][i][j]=min(d[k-1][i][j],d[k-1][i][k]+d[k-1][k][j])。显然,这个DP也可以使用二维数组不断进行d[i][j]=min(d[i][j],d[i][k]+d[k][j])的更新来实现
这就是Floyd-Warshall算法,复杂度O(|V|3),可以处理边是负数的情况。判断图中是否有负圈只需检查是否存在d[i][i]是负数的顶点i就可以了。

 1 #include <cstdio>
 2 #include <cstring>
 3 
 4 using namespace std;
 5 
 6 const int INF=0x3f3f3f3f;
 7 int V,E,S;
 8 int **d;
 9 
10 void floyd();
11 int min(int, int);
12 
13 int main()
14 {
15     scanf("%d %d %d", &V, &E, &S);
16     --S;
17     d=new int*[V];
18     for (int i=0; i<=V; i++)
19     {
20         d[i]=new int[V];
21         memset(d[i],0x3f,sizeof(int)*(V));
22     }
23     for (int i=0; i<E; i++)
24     {
25         int s,t,c;
26         scanf("%d %d %d", &s, &t, &c);
27         d[s-1][t-1]=min(d[s-1][t-1],c);
28     }
29     for (int i=0; i<V; i++) d[i][i]=0;
30     floyd();
31     for (int i=0; i<V; i++)
32         if (d[S][i]!=INF) printf("%d ", d[S][i]);
33         else printf("%d ", 2147483647);
34 }
35 
36 int min(int x, int y)
37 {
38     if (x<y) return x;
39     return y;
40 }
41 
42 void floyd()
43 {
44     for (int k=0; k<V; k++)
45     {
46         for (int i=0; i<V; i++)
47         {
48             for (int j=0; j<V; j++)
49             {
50                 d[i][j] = min(d[i][j],d[i][k]+d[k][j]);
51             }
52         }
53     }
54 }

路径还原
当需要求解最短路的路径时,不断寻找前趋节点就可以恢复出最短路。用prev[j]来记录最短路上顶点j的前趋,呢么就可以在O(|V|)的时间内完成最短路的恢复。在d[j]被d[j]=d[k]+cost[k][j]更新时,修改prev[j]=k,这样就可以得到prev数组,在计算从s出发到j的最短路时,通过prev[j]就可以知道顶点j的前趋,因此不断把j替换成prev[j]直到j=s为止就可以了。

1 vector<int> get_path(int t)
2 {
3     vector<int> path;
4     for (; t!=-1; t=prev[t]) path.push_back(t);//path[]初始值全为-1 
5     reverse(path.begin(),path.end());
6     return path;
7 }

 最小生成树(MST, Minimum Spanning Tree)【生成树是否存在和图是否连通是等价的】


最小生成树问题1(Prim算法)

  • 首先,假设有一棵只包含一个顶点v的树T,然后贪心地选取T和其它顶点之间相连的最小权值的边,并把它加到T中,不断进行这个操作,就可以得到一棵生成树了,可证这是一棵最小生成树
  • 如何查找最小权值的边?假设已经求得的生成树的顶点的集合是X,把X集合和顶点v连接的边的最小权值记为mincost[v],在向X里添加顶点u时,只需要查看和u相连的边就可以了,对于每条边,更新mincost[v]=min(mincost[v],边(u,v)的权值)
    如果每次都遍历为包含在X中的点的mincost[v],需要O(|V|2)的时间,不过和Dijkstra一样我们可以使用堆来维护mincost,时间复杂度就是O(|E|log|V|)
     1 #include <cstdio>
     2 #include <queue>
     3 #include <vector>
     4 #include <functional>
     5 #define num s-'0'
     6 
     7 using namespace std;
     8 
     9 typedef pair<int, int> P;
    10 struct edge
    11 {
    12     int to;
    13     int cost;
    14 };
    15 
    16 const int MAX_V=5000;
    17 const int INF=0x3f3f3f3f;
    18 int V,E,n;
    19 vector<edge> G[MAX_V];
    20 int res;
    21 int mincost[MAX_V];
    22 bool used[MAX_V];
    23 
    24 void prim();
    25 
    26 void read(int &x){
    27     char s;
    28     x=0;
    29     bool flag=0;
    30     while(!isdigit(s=getchar()))
    31         (s=='-')&&(flag=true);
    32     for(x=num;isdigit(s=getchar());x=x*10+num);
    33     (flag)&&(x=-x);
    34 }
    35 
    36 void write(int x)
    37 {
    38     if(x<0)
    39     {
    40         putchar('-');
    41         x=-x;
    42     }
    43     if(x>9)
    44         write(x/10);
    45     putchar(x%10+'0');
    46 }
    47 
    48 int main()
    49 {
    50     read(V);
    51     read(E);
    52     for (int i=0; i<E; i++)
    53     {
    54         int s,t,c;
    55         read(s);read(t);read(c);
    56         edge e;
    57         e.to=t-1;e.cost=c;
    58         G[s-1].push_back(e);
    59         e.to=s-1;e.cost=c;
    60         G[t-1].push_back(e);
    61     }
    62     prim();
    63     if (n<V) puts("orz");
    64     else write(res);
    65     putchar('\n');
    66 }
    67 
    68 void prim()
    69 {
    70     for (int i=0; i<V; i++)
    71     {
    72         mincost[i]=INF;
    73         used[i]=false;
    74     }
    75     mincost[0]=0;
    76     priority_queue<P, vector<P>, greater<P>> que;
    77     que.push(P(0,0));
    78     while (!que.empty())
    79     {
    80         P p=que.top(); que.pop();
    81         int v=p.second;
    82         if (mincost[v]<p.first) continue;
    83         res+=mincost[v];++n;used[v]=true;
    84         for (int i=0; i<G[v].size(); i++)
    85         {
    86         if (mincost[G[v][i].to]>G[v][i].cost && !used[G[v][i].to])
    87             {
    88                 mincost[G[v][i].to]=G[v][i].cost;
    89                 que.push(P(G[v][i].cost,G[v][i].to));
    90             }
    91         }
    92     }
    93 }

     这段代码使用了读入优化和输出优化,并且在遇到图不连通的情况时输出orz


最小生成树问题2(Kruskal算法)

  • Kruskal算法按照边的权值的大小从小到大查看一遍,如果不产生圈(重边等也算在内),就把当前这条边加入到生成树中,一共加V-1条边。
  • 如何判断是否产生圈? 假设现在要把连接顶点u和顶点v的边e加入生成树中。如果加入之前u和v不在同一个连通分量里,那么加入e也不会产生圈,反之,如果u和v在同一个连通分量里,那么一定会产生圈,可以用并查集高效地判断是否属于同一个连通分量
  • Kruskal在边的排序上最费时,算法的复杂度是O(|E|log|V|).
      1 #include <cstdio>
      2 #include <queue>
      3 #include <vector>
      4 #include <algorithm>
      5 #include <cctype>
      6 #define num s-'0'
      7 
      8 using namespace std;
      9 
     10 struct edge
     11 {
     12     int u;
     13     int v;
     14     int cost;
     15 };
     16 
     17 const int MAX_V=5000;
     18 const int MAX_E=200000;
     19 const int INF=0x3f3f3f3f;
     20 int V,E,n;
     21 edge es[MAX_E];
     22 int res;
     23 int par[MAX_V];
     24 int r[MAX_V];
     25 
     26 void kruskal();
     27 void init();
     28 int find(int);
     29 void unite(int, int);
     30 bool same(int, int);
     31 
     32 void read(int &x){
     33     char s;
     34     x=0;
     35     bool flag=0;
     36     while(!isdigit(s=getchar()))
     37         (s=='-')&&(flag=true);
     38     for(x=num;isdigit(s=getchar());x=x*10+num);
     39     (flag)&&(x=-x);
     40 }
     41 
     42 void write(int x)
     43 {
     44     if(x<0)
     45     {
     46         putchar('-');
     47         x=-x;
     48     }
     49     if(x>9)
     50         write(x/10);
     51     putchar(x%10+'0');
     52 }
     53 
     54 bool compare(const edge &e1, const edge &e2)
     55 {
     56     return e1.cost<e2.cost;
     57 }
     58 
     59 int main()
     60 {
     61     read(V);read(E);
     62     for (int i=0; i<E; i++)
     63     {
     64         read(es[i].u);read(es[i].v);read(es[i].cost);
     65         es[i].u--;es[i].v--;
     66     }
     67     kruskal();
     68     write(res);
     69     putchar('\n');
     70 }
     71 
     72 void init()
     73 {
     74     for (int i=0; i<V; i++)
     75     {
     76         par[i]=i;
     77         r[i]=0;
     78     }
     79 }
     80 
     81 int find(int x)
     82 {
     83     if (par[x]==x) return x;
     84     return par[x]=find(par[x]);
     85 }
     86 
     87 void unite(int x, int y)
     88 {
     89     x=find(x);
     90     y=find(y);
     91     if (x==y) return;
     92     if (r[x]<r[y]) par[x]=y;
     93     else
     94     {
     95         par[y]=x;
     96         if (r[x]==r[y]) r[x]++; 
     97     } 
     98 }
     99 
    100 bool same(int x, int y)
    101 {
    102     return (find(x)==find(y));
    103 }
    104 
    105 void kruskal()
    106 {
    107     sort(es, es+E, compare);
    108     init();
    109     int n=0;
    110     for (int i=0; i<E; i++)
    111     {
    112         edge e=es[i];
    113         if (!same(e.u, e.v))
    114         {
    115             unite(e.u, e.v);
    116             res+=e.cost;
    117             ++n;
    118         }
    119         if (n==V-1) break;
    120     }
    121 }

      


Roadblocks(POJ 3255)

  • 原题如下:
    Roadblocks
    Time Limit: 2000MS Memory Limit: 65536K
    Total Submissions: 19299 Accepted: 6773

    Description

    Bessie has moved to a small farm and sometimes enjoys returning to visit one of her best friends. She does not want to get to her old home too quickly, because she likes the scenery along the way. She has decided to take the second-shortest rather than the shortest path. She knows there must be some second-shortest path.

    The countryside consists of R (1 ≤ R ≤ 100,000) bidirectional roads, each linking two of the N (1 ≤ N ≤ 5000) intersections, conveniently numbered 1..N. Bessie starts at intersection 1, and her friend (the destination) is at intersection N.

    The second-shortest path may share roads with any of the shortest paths, and it may backtrack i.e., use the same road or intersection more than once. The second-shortest path is the shortest path whose length is longer than the shortest path(s) (i.e., if two or more shortest paths exist, the second-shortest path is the one whose length is longer than those but no longer than any other path).

    Input

    Line 1: Two space-separated integers:  N and  R
    Lines 2.. R+1: Each line contains three space-separated integers:  AB, and  D that describe a road that connects intersections  A and  B and has length  D (1 ≤  D ≤ 5000)

    Output

    Line 1: The length of the second shortest path between node 1 and node  N

    Sample Input

    4 4
    1 2 100
    2 4 200
    2 3 250
    3 4 100

    Sample Output

    450

    Hint

    Two routes: 1 -> 2 -> 4 (length 100+200=300) and 1 -> 2 -> 3 -> 4 (length 100+250+100=450)
  • 题解:这题要求的是次短路,根据Dijkstra算法的思路,依次确定尚未确定的顶点中距离最小的顶点可求得最短路,在这个基础上做少许修改,即可求得次短路,到某个顶点v的次短路无非两种情况:①到其它顶点u的最短路再加上u→v的边,②到u的次短路再加上u→v的边。因此,只要求出到所有顶点的最短路和次短路就好了。对于每个顶点,我们记录最短路和次短路,类似Dijkstra算法那样,不断更新这两个距离。(注:这道题中男女之间的二分图结构是无用的陷阱条件,许多问题中,如果有特殊的结构,往往会考虑如何利用这个结构,但这题不是的)
  • 代码:
      1 #include <cstdio>
      2 #include <queue>
      3 #include <vector>
      4 #include <functional>
      5 #include <cctype>
      6 #include <algorithm>
      7 #define num s-'0'
      8 
      9 using namespace std;
     10 typedef pair<int, int> P;
     11 
     12 struct edge
     13 {
     14     int to;
     15     int cost;
     16 };
     17 
     18 const int MAX_V=5000;
     19 const int INF=0x3f3f3f3f;
     20 int V,E,n;
     21 vector<edge> G[MAX_V];
     22 int dist[MAX_V];
     23 int dist2[MAX_V];
     24 
     25 void solve();
     26 
     27 void swap(int &x, int &y)
     28 {
     29     int t=x;
     30     x=y;y=t;
     31 }
     32 
     33 void read(int &x){
     34     char s;
     35     x=0;
     36     bool flag=0;
     37     while(!isdigit(s=getchar()))
     38         (s=='-')&&(flag=true);
     39     for(x=num;isdigit(s=getchar());x=x*10+num);
     40     (flag)&&(x=-x);
     41 }
     42 
     43 void write(int x)
     44 {
     45     if(x<0)
     46     {
     47         putchar('-');
     48         x=-x;
     49     }
     50     if(x>9)
     51         write(x/10);
     52     putchar(x%10+'0');
     53 }
     54 
     55 int main()
     56 {
     57     read(V);read(E);
     58     for (int i=0; i<E; i++)
     59     {
     60         edge e;
     61         int s,t,c;
     62         read(s);read(t);read(c);
     63         e.to=t-1;e.cost=c;
     64         G[s-1].push_back(e);
     65         e.to=s-1;e.cost=c;
     66         G[t-1].push_back(e);
     67     }
     68     solve();
     69     write(dist2[V-1]);
     70     putchar('\n');
     71 }
     72 
     73 void solve()
     74 {
     75     priority_queue<P, vector<P>, greater<P> > que;
     76     fill(dist, dist+V, INF);
     77     fill(dist2, dist2+V, INF);
     78     dist[0]=0;
     79     que.push(P(0,0));
     80     while (!que.empty())
     81     {
     82         P p=que.top(); que.pop();
     83         int v=p.second, d=p.first;
     84         if (dist2[v]<d) continue;
     85         for (int i=0; i<G[v].size(); i++)
     86         {
     87             edge &e=G[v][i];
     88             int d2=d+e.cost;
     89             if (dist[e.to]>d2)
     90             {
     91                 swap(dist[e.to],d2);
     92                 que.push(P(dist[e.to],e.to));
     93             }
     94             if (dist2[e.to]>d2 && dist[e.to]<d2)
     95             {
     96                 dist2[e.to]=d2;
     97                 que.push(P(dist2[e.to],e.to));
     98             }
     99         }
    100     }
    101 }
    Roadblocks

Conscription(POJ 3723)

  • 原题如下:
    Conscription
    Time Limit: 1000MS Memory Limit: 65536K
    Total Submissions: 16578 Accepted: 5762

    Description

    Windy has a country, and he wants to build an army to protect his country. He has picked up N girls and M boys and wants to collect them to be his soldiers. To collect a soldier without any privilege, he must pay 10000 RMB. There are some relationships between girls and boys and Windy can use these relationships to reduce his cost. If girl x and boy y have a relationship d and one of them has been collected, Windy can collect the other one with 10000-d RMB. Now given all the relationships between girls and boys, your assignment is to find the least amount of money Windy has to pay. Notice that only one relationship can be used when collecting one soldier.

    Input

    The first line of input is the number of test case.
    The first line of each test case contains three integers, NM and R.
    Then R lines followed, each contains three integers xiyi and di.
    There is a blank line before each test case.

    1 ≤ NM ≤ 10000
    0 ≤ R ≤ 50,000
    0 ≤ xi < N
    0 ≤ yi < M
    0 < di < 10000

    Output

    For each test case output the answer in a single line.

    Sample Input

    2
    
    5 5 8
    4 3 6831
    1 3 4583
    0 0 6592
    0 1 3063
    3 3 4975
    1 3 2049
    4 2 2104
    2 2 781
    
    5 5 10
    2 4 9820
    3 2 6236
    3 1 8864
    2 4 8326
    2 0 5156
    2 0 1463
    4 1 2439
    0 4 4373
    3 4 8889
    2 4 3133
    

    Sample Output

    71071
    54223
  • 题解:设想这样一个无向图:在征募某个人a时,如果使用了a和b之间的关系,那么就连一条a到b的边,假设这个图中存在圈,那么无论以什么顺序征募这个圈上的所有人,都会产生矛盾,即由于圈的存在,矛盾是必然的,由此可以知道,这个图应该是一片森林。反过来,给定一片森林里,必然可以使用对应的关系确定征募的顺序。综上,把人看作点,关系看作边,这个问题就可以转化为求解无向图中的最大权森林问题,最大权森林问题可以通过把所有边权取反后用最小生成树的算法求解
  • 代码:
      1 #include <cstdio>
      2 #include <queue>
      3 #include <vector>
      4 #include <algorithm>
      5 #include <cctype>
      6 #define num s-'0'
      7 
      8 using namespace std;
      9 
     10 struct edge
     11 {
     12     int u;
     13     int v;
     14     int cost;
     15 };
     16 
     17 const int MAX_V=30000;
     18 const int MAX_E=60000;
     19 const int INF=0x3f3f3f3f;
     20 int K,N, M, E, V;
     21 edge es[MAX_E];
     22 long long res;
     23 int par[MAX_V];
     24 int r[MAX_V];
     25 
     26 void kruskal();
     27 void init();
     28 int find(int);
     29 void unite(int, int);
     30 bool same(int, int);
     31 
     32 void read(int &x){
     33     char s;
     34     x=0;
     35     bool flag=0;
     36     while(!isdigit(s=getchar()))
     37         (s=='-')&&(flag=true);
     38     for(x=num;isdigit(s=getchar());x=x*10+num);
     39     (flag)&&(x=-x);
     40 }
     41 
     42 void write(int x)
     43 {
     44     if(x<0)
     45     {
     46         putchar('-');
     47         x=-x;
     48     }
     49     if(x>9)
     50         write(x/10);
     51     putchar(x%10+'0');
     52 }
     53 
     54 bool compare(const edge &e1, const edge &e2)
     55 {
     56     return e1.cost<e2.cost;
     57 }
     58 
     59 int main()
     60 {
     61     read(K);
     62     for (int j=0; j<K; j++)
     63     {
     64         res=0;
     65         read(N);read(M);read(E);V=N+M;
     66         for (int i=0; i<E; i++)
     67         {
     68             read(es[i].u);read(es[i].v);read(es[i].cost);
     69             es[i].v+=N;es[i].cost=-es[i].cost;
     70         }
     71         kruskal();
     72         write(10000*(N+M)+res);
     73         putchar('\n');
     74     }
     75 }
     76 
     77 void init()
     78 {
     79     for (int i=0; i<V; i++)
     80     {
     81         par[i]=i;
     82         r[i]=0;
     83     }
     84 }
     85 
     86 int find(int x)
     87 {
     88     if (par[x]==x) return x;
     89     return par[x]=find(par[x]);
     90 }
     91 
     92 void unite(int x, int y)
     93 {
     94     x=find(x);
     95     y=find(y);
     96     if (x==y) return;
     97     if (r[x]<r[y]) par[x]=y;
     98     else
     99     {
    100         par[y]=x;
    101         if (r[x]==r[y]) r[x]++; 
    102     } 
    103 }
    104 
    105 bool same(int x, int y)
    106 {
    107     return (find(x)==find(y));
    108 }
    109 
    110 void kruskal()
    111 {
    112     sort(es, es+E, compare);
    113     init();
    114     for (int i=0; i<E; i++)
    115     {
    116         edge e=es[i];
    117         if (!same(e.u, e.v))
    118         {
    119             unite(e.u, e.v);
    120             res+=e.cost;
    121         }
    122     }
    123 }
    Conscription

Layout(POJ 3169)

  • 原题如下:
    Layout
    Time Limit: 1000MS   Memory Limit: 65536K
    Total Submissions: 14477   Accepted: 6956

    Description

    Like everyone else, cows like to stand close to their friends when queuing for feed. FJ has N (2 <= N <= 1,000) cows numbered 1..N standing along a straight line waiting for feed. The cows are standing in the same order as they are numbered, and since they can be rather pushy, it is possible that two or more cows can line up at exactly the same location (that is, if we think of each cow as being located at some coordinate on a number line, then it is possible for two or more cows to share the same coordinate). 

    Some cows like each other and want to be within a certain distance of each other in line. Some really dislike each other and want to be separated by at least a certain distance. A list of ML (1 <= ML <= 10,000) constraints describes which cows like each other and the maximum distance by which they may be separated; a subsequent list of MD constraints (1 <= MD <= 10,000) tells which cows dislike each other and the minimum distance by which they must be separated. 

    Your job is to compute, if possible, the maximum possible distance between cow 1 and cow N that satisfies the distance constraints.

    Input

    Line 1: Three space-separated integers: N, ML, and MD. 

    Lines 2..ML+1: Each line contains three space-separated positive integers: A, B, and D, with 1 <= A < B <= N. Cows A and B must be at most D (1 <= D <= 1,000,000) apart. 

    Lines ML+2..ML+MD+1: Each line contains three space-separated positive integers: A, B, and D, with 1 <= A < B <= N. Cows A and B must be at least D (1 <= D <= 1,000,000) apart.

    Output

    Line 1: A single integer. If no line-up is possible, output -1. If cows 1 and N can be arbitrarily far apart, output -2. Otherwise output the greatest possible distance between cows 1 and N.

    Sample Input

    4 2 1
    1 3 10
    2 4 20
    2 3 3

    Sample Output

    27

    Hint

    Explanation of the sample: 

    There are 4 cows. Cows #1 and #3 must be no more than 10 units apart, cows #2 and #4 must be no more than 20 units apart, and cows #2 and #3 dislike each other and must be no fewer than 3 units apart. 

    The best layout, in terms of coordinates on a number line, is to put cow #1 at 0, cow #2 at 7, cow #3 at 10, and cow #4 at 27.
  • 题解:记第i号牛的位置d[i],首先,牛是按照编号顺序排列的,所以有d[i]≤d[i+1]成立,其次,对于每对关系好的牛之间的最大距离限制,都有d[AL]+DL≥d[BL]成立,同样对于每对关系不好的牛,都有d[AD]+DD≤d[BD]成立,因此,原问题就转化为了在满足这三类不等式的情况下,求解d[N]-d[1]的最大值的问题。这种线性规划问题当然可以用单纯形法来求,但这道题还比较特殊,这些不等式的两边都只有1个变量,这种特殊形式的不等式方程组又叫做差分约束系统,它可以用我们熟悉的图论最短路算法来求。最短路问题可以用这种不等式的形式表示出来,记从起点s出发,到各个顶点v的最短距离为d(v),因此,对于每条权值为w的边e=(v,u),都有d(v)+w≥d(u)成立,在满足全部这些约束不等式中的d中,d(v)-d(s)的最大值就是从s到v的最短距离。对比差分约束与最短路问题,两者具有完全一样的形式,所以,可以把原来的问题的每一个约束不等式对应成图中的一条边来构图,然后通过解决最短路问题来解决原问题。首先把顶点编号为1~N,d[i]≤d[i+1]变形为d[i+1]+0≥d[i],因此,从顶点i+1向顶点i连一条权值为0的边,同样d[AL]+DL≥d[BL]对应从顶点AL向顶点BL连一条权值为DL的边,d[AD]+DD≤d[BD]对应从BD向顶点AD连一条权值为-DD的边。所求的问题是d[N]-d[1]的最大值,对应为顶点1到顶点N的最短距离。
  • 代码:
      1 #include <cstdio>
      2 #include <queue>
      3 #include <cctype>
      4 #define num s-'0'
      5 using namespace std;
      6 
      7 struct edge
      8 {
      9     int to;
     10     int cost;
     11 };
     12 
     13 const int INF=0x3f3f3f3f;
     14 const int MAX_V=1000;
     15 vector<edge> G[MAX_V];
     16 int V, ML, MD;
     17 int d[MAX_V];
     18 bool inque[MAX_V];
     19 int n[MAX_V];
     20 
     21 void read(int &x){
     22     char s;
     23     x=0;
     24     bool flag=0;
     25     while(!isdigit(s=getchar()))
     26         (s=='-')&&(flag=true);
     27     for(x=num;isdigit(s=getchar());x=x*10+num);
     28     (flag)&&(x=-x);
     29 }
     30 
     31 void write(int x)
     32 {
     33     if(x<0)
     34     {
     35         putchar('-');
     36         x=-x;
     37     }
     38     if(x>9)
     39         write(x/10);
     40     putchar(x%10+'0');
     41 }
     42 
     43 bool spfa();
     44 
     45 int main()
     46 {
     47     read(V); read(ML); read(MD);
     48     for (int i=0; i<ML; i++)
     49     {
     50         int A, B, C;
     51         read(A); read(B); read(C);
     52         edge e;
     53         e.to=B-1;e.cost=C;
     54         G[A-1].push_back(e);
     55     }
     56     for (int i=0; i<MD; i++)
     57     {
     58         int A, B, C;
     59         read(A); read(B); read(C);
     60         edge e;
     61         e.to=A-1;e.cost=-C;
     62         G[B-1].push_back(e);
     63     }
     64     for (int i=0; i<V-1; i++)
     65     {
     66         edge e;
     67         e.to=i;e.cost=0;
     68         G[i+1].push_back(e);
     69     }
     70     if (spfa())
     71     {
     72         if (d[V-1]==INF) write(-2);
     73         else write(d[V-1]);
     74     }
     75     else write(-1);
     76 }
     77 
     78 bool spfa()
     79 {
     80     fill(d, d+V, INF);
     81     fill(inque, inque+V, false);
     82     fill(n, n+V, 0);
     83     queue<int> que;
     84     d[0]=0;
     85     que.push(0);
     86     n[0]++;
     87     inque[0]=true;
     88     while (!que.empty())
     89     {
     90         int v=que.front();que.pop();
     91         inque[v]=false;
     92         for (int i=0; i<G[v].size(); i++)
     93         {
     94             if (d[G[v][i].to]>d[v]+G[v][i].cost)
     95             {
     96                 d[G[v][i].to]=d[v]+G[v][i].cost;
     97                 if (!inque[G[v][i].to]) 
     98                 {
     99                     que.push(G[v][i].to);
    100                     inque[G[v][i].to]=true;
    101                     if (++n[G[v][i].to]>V) return false;
    102                 }
    103             }
    104         }
    105     }
    106     return true;
    107 }
    Layout

猜你喜欢

转载自www.cnblogs.com/Ymir-TaoMee/p/9448406.html