题意:
一群动物想从动物园逃离,从一个图的左上角出发,只有到达右下角才能彻底逃离。
动物园的管理者为了防止他们逃跑,安排了若干人在路上防守。
现在问最少需要花多少人能够封锁动物逃跑的路线。
思路:
简单画一画知道,就是找一个边的集合把整个图分成两个部分,且要求这些边的权值最小,这是著名的最小割问题(虽然我没有学过。
不过可以用最短路解决。
经过尝试可以知道,从下到上的连线,从左到右的连线,从左到上的连线以及从下到右的连线均可以到达目的,其它的就不行,比如从下到左。
所以,可以以每一条边作为一个点,把左边界的边和下边界的边作为起点,右边界和上边界作为终点,求出最短路即是答案。
接下来就是连边的问题,如果i和j之间连边,那么从i连向j,那么这条边的权值就是i边的权值(这时候是把边当作点的);若是从j向i连边,那么就是j边的权值作为这条边的权值。
最后是哪些些边之间要连边的问题,处在同一个小三角形当中的边两两之间都要连边。
一开始以为只有对角线要和两条直角边连线,最后画图发现其实两条直角边互相连线也是必须的。
如图:(自己画的,很丑)
这题用到了超级源点的技巧,然后优先队列优化的Dijkstra。
最难的还是如何给边编号的问题,我所用的编号就是题目的输入顺序,水平边和竖直边以及对角线的编号之间存在着一定的关系,可以推导得出,只不过比较繁琐,所以代码略长。
代码:
1 #include <stdio.h> 2 #include <string.h> 3 #include <algorithm> 4 #include <vector> 5 #include <queue> 6 using namespace std; 7 8 const int N = 5e6; 9 const int inf = 0x3f3f3f3f; 10 11 typedef pair<int,int> pii; 12 13 int d[N]; 14 int w[N]; 15 16 struct edge 17 { 18 int to,cost; 19 edge(){}; 20 edge(int a,int b) 21 { 22 to = a; 23 cost = b; 24 } 25 }; 26 27 vector<edge> es; 28 vector<int> g[N]; 29 30 void adde(int from,int to,int cost) 31 { 32 es.push_back(edge(to,cost)); 33 g[from].push_back(es.size() - 1); 34 } 35 36 void init(int n) 37 { 38 es.clear(); 39 40 for (int i = 0;i <= n;i++) g[i].clear(); 41 42 memset(d,inf,sizeof(d)); 43 } 44 45 void dij(int n) 46 { 47 priority_queue<pii,vector<pii>,greater<pii> > pq; 48 49 d[0] = 0; 50 51 pq.push(pii(0,0)); 52 53 while (!pq.empty()) 54 { 55 pii x = pq.top();pq.pop(); 56 57 int v = x.second; 58 59 if (d[v] < x.first) continue; 60 61 for (int i = 0;i < g[v].size();i++) 62 { 63 int id = g[v][i]; 64 65 edge e = es[id]; 66 67 if (d[e.to] > e.cost + d[v]) 68 { 69 d[e.to] = d[v] + e.cost; 70 pq.push(pii(d[e.to],e.to)); 71 } 72 } 73 } 74 } 75 76 int main() 77 { 78 int n,m; 79 int kase = 0; 80 81 while (scanf("%d%d",&n,&m) != EOF) 82 { 83 if (m == 0 && n == 0) break; 84 85 int cnt = 0; 86 87 //init(n,m); 88 89 for (int i = 0;i < n;i++) 90 { 91 for (int j = 0;j < m - 1;j++) 92 { 93 int tmp; 94 95 scanf("%d",&tmp); 96 97 w[++cnt] = tmp; 98 } 99 } 100 101 for (int i = 0;i < n - 1;i++) 102 { 103 for (int j = 0;j < m;j++) 104 { 105 int tmp; 106 scanf("%d",&tmp); 107 w[++cnt] = tmp; 108 } 109 } 110 111 for (int i = 0;i < n - 1;i++) 112 { 113 for (int j = 0;j < m - 1;j++) 114 { 115 int tmp; 116 scanf("%d",&tmp); 117 w[++cnt] = tmp; 118 } 119 } 120 121 int rs = 1,cs = n * (m - 1) + 1,ds = n * (m - 1) + m * (n - 1) + 1; 122 123 //int sume = n * (m - 1) + m * (n - 1) + (m - 1) * (n - 1); 124 125 init(cnt); 126 127 for (int i = ds;i <= cnt;i++) 128 { 129 int num = (i - ds) / (m - 1); 130 int curds = ds + (m - 1) * num; 131 int curcs = cs + num * m; 132 int currs = rs + num * (m - 1); 133 int tmp = i - curds; 134 int curc = curcs + tmp,curr = currs + tmp; 135 136 adde(curc,i,w[curc]); 137 adde(i,curc,w[i]); 138 adde(i,curc + 1,w[i]); 139 adde(curc+1,i,w[curc+1]); 140 adde(i,curr,w[i]); 141 adde(curr,i,w[curr]); 142 adde(curr + m - 1,i,w[curr + m - 1]); 143 adde(i,curr + m - 1,w[i]); 144 adde(curc,curr+m-1,w[curc]); 145 adde(curr + m - 1,curc,w[curr + m - 1]); 146 adde(curr,curc + 1,w[curr]); 147 adde(curc+1,curr,w[curc+1]); 148 } 149 150 for (int i = 0;i < n - 1;i++) 151 { 152 adde(0,cs + i * m,0); 153 } 154 155 for (int i = 0;i < m - 1;i++) 156 { 157 adde(0,(n - 1) * (m - 1) + 1 + i,0); 158 } 159 160 dij(cnt); 161 162 int ans = inf; 163 164 for (int i = 1;i <= m - 1;i++) 165 { 166 ans = min(ans,d[i] + w[i]); 167 } 168 169 for (int i = 1;i <= n - 1;i++) 170 { 171 int v = n * (m - 1) + m * i; 172 ans = min(ans,d[v] + w[v]); 173 } 174 175 printf("Case %d: Minimum = %d ",++kase,ans); 176 } 177 178 return 0; 179 }