Description

Gabiluso is one of the greatest spies in his country. Now he’s trying to complete an “impossible” mission ----- to make it slow for the army of City Colugu to reach the airport. City Colugu has n bus stations and m roads. Each road connects two bus stations directly, and all roads are one way streets. In order to keep the air clean, the government bans all military vehicles. So the army must take buses to go to the airport. There may be more than one road between two bus stations. If a bus station is destroyed, all roads connecting that station will become no use. What’s Gabiluso needs to do is destroying some bus stations to make the army can’t get to the airport in k minutes. It takes exactly one minute for a bus to pass any road. All bus stations are numbered from 1 to n. The No.1 bus station is in the barrack and the No. n station is in the airport. The army always set out from the No. 1 station. No.1 station and No. n station can’t be destroyed because of the heavy guard. Of course there is no road from No.1 station to No. n station.
Please help Gabiluso to calculate the minimum number of bus stations he must destroy to complete his mission.
 

Input

There are several test cases. Input ends with three zeros.
For each test case:
The first line contains 3 integers, n, m and k. (0< n <=50, 0< m<=4000, 0 < k < 1000) Then m lines follows. Each line contains 2 integers, s and f, indicating that there is a road from station No. s to station No. f. 
 

Output

For each test case, output the minimum number of stations Gabiluso must destroy.

题目大意:有n(n≤50)个点,起点1到终点n,有m条有向边(m≤4000)。现破坏掉若干起点和终点以外的点,使得从起点到终点经过的边数必须大于k条。问最少要破坏多少个点,保证从起点到终点没有边。

我们先来看一个可以AC但实际上错误的思路o(╯□╰)o(为什么错误还能AC啊?数据弱呗……)

思路:先求每个点到起点和终点的最短路径,然后每个点拆成两个点x、x',如果dis(s,x) + dis(x,t) ≤ k,那么建一条边x→x',容量为1(源点和汇点容量为无穷大)。对每条边(i, j),连一条边i'→j,容量为无穷大。求最小割。根据最大流最小割定理,最大流为答案。因为对于一点x,如果dis(s,x) + dis(x,t) > k,那么没必要破坏点x。那么问题就变成了最少破坏多少个点,使得从1到n必须要经过一个点,经过那个点的话从1到n必然会大于k。

先上代码(15MS):

 #include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std; const int MAXN = ;
const int MAXE = ;
const int INF = 0x3fff3fff; struct SAP {
int head[MAXN], cur[MAXN], pre[MAXN], gap[MAXN], dis[MAXN];
int to[MAXE], cap[MAXE], flow[MAXE], next[MAXE];
int ecnt, n, st, ed; void init() {
memset(head, , sizeof(head));
ecnt = ;
} void add_edge(int u, int v, int c) {
to[ecnt] = v; cap[ecnt] = c; flow[ecnt] = ; next[ecnt] = head[u]; head[u] = ecnt++;
to[ecnt] = u; cap[ecnt] = ; flow[ecnt] = ; next[ecnt] = head[v]; head[v] = ecnt++;
//printf("%d->%d %d\n", u, v, c);
} void bfs() {
memset(dis, 0x3f, sizeof(dis));
queue<int> que; que.push(ed);
dis[ed] = ;
while(!que.empty()) {
int u = que.front(); que.pop();
++gap[dis[u]];
for(int p = head[u]; p; p = next[p]) {
int v = to[p];
if(dis[v] > n && cap[p ^ ]) {
dis[v] = dis[u] + ;
que.push(v);
}
}
}
} int Max_flow(int ss, int tt, int nn) {
st = ss, ed = tt, n = nn;
int ans = , minFlow = INF, u;
for(int i = ; i <= n; ++i) {
cur[i] = head[i];
gap[i] = ;
}
u = pre[st] = st;
bfs();
while(dis[st] < n) {
bool flag = false;
for(int &p = cur[u]; p; p = next[p]) {
int v = to[p];
if(cap[p] > flow[p] && dis[v] + == dis[u]) {
flag = true;
minFlow = min(minFlow, cap[p] - flow[p]);
pre[v] = u;
u = v;
if(u == ed) {
ans += minFlow;
while(u != st) {
u = pre[u];
flow[cur[u]] += minFlow;
flow[cur[u] ^ ] -= minFlow;
}
minFlow = INF;
}
break;
}
}
if(flag) continue;
int minDis = n - ;
for(int p = head[u]; p; p = next[p]) {
int v = to[p];
if(cap[p] > flow[p] && dis[v] < minDis) {
minDis = dis[v];
cur[u] = p;
}
}
if(--gap[dis[u]] == ) break;
gap[dis[u] = minDis + ]++;
u = pre[u];
}
return ans;
}
} G; struct SP {
int head[MAXN], head2[MAXN], dis_st[MAXN], dis_ed[MAXN];
int to[MAXE], next[MAXE], to2[MAXE], next2[MAXE];
int ecnt, n, st, ed; void init(int ss, int tt, int nn) {
memset(head, , sizeof(head));
memset(head2, , sizeof(head2));
ecnt = ;
st = ss; ed = tt; n = nn;
} void add_edge(int u, int v) {
to[ecnt] = v; next[ecnt] = head[u]; head[u] = ecnt;
to2[ecnt] = u; next2[ecnt] = head2[v]; head2[v] = ecnt++;
} void make_dis_st() {
memset(dis_st, 0x3f, sizeof(dis_st));
queue<int> que; que.push(st);
dis_st[st] = ;
while(!que.empty()) {
int u = que.front(); que.pop();
for(int p = head[u]; p; p = next[p]) {
int v = to[p];
if(dis_st[v] > n) {
dis_st[v] = dis_st[u] + ;
que.push(v);
}
}
}
} void make_dis_ed() {
memset(dis_ed, 0x3f, sizeof(dis_ed));
queue<int> que; que.push(ed);
dis_ed[ed] = ;
while(!que.empty()) {
int u = que.front(); que.pop();
for(int p = head2[u]; p; p = next2[p]) {
int v = to2[p];
if(dis_ed[v] > n) {
dis_ed[v] = dis_ed[u] + ;
que.push(v);
}
}
}
} void make_G(int k) {
make_dis_st();
//for(int i = 1; i <= n; ++i) printf("%d ", dis_st[i]);
make_dis_ed();
//for(int i = 1; i <= n; ++i) printf("%d ", dis_ed[i]);
G.init();
G.add_edge(, + n, INF);
G.add_edge(n, n + n, INF);
for(int i = ; i < n; ++i)
if(dis_st[i] + dis_ed[i] <= k) G.add_edge(i, i + n, );
for(int u = ; u <= n; ++u) {
for(int p = head[u]; p; p = next[p]) {
int v = to[p];
G.add_edge(u + n, v, INF);
}
}
}
} T; int n, m, k, a, b; int main() {
while(scanf("%d%d%d", &n, &m, &k) != EOF) {
if(n == && m == && k == ) break;
T.init(, n, n);
while(m--) {
scanf("%d%d", &a, &b);
T.add_edge(a, b);
}
T.make_G(k);
printf("%d\n", G.Max_flow(, n + n, n + n));
}
}

但是这样做是错的,为什么呢?我们来看一个Discuss里的数据:

8 10 5
1 2
2 3
3 4
4 5
5 6
6 8
1 7
7 8
4 7
7 4
这个数据输出应该是1(破坏点7),但是上面的代码会输出2。因为上面的思路忽略了一点:当我们破坏掉某个点的时候,经过另一些从起点到终点的距离可能会变化以至于大于k。

那么怎么办呢?我们只能退而求其次o(╯□╰)o,虽然这也是一个能AC但是错的思路

思路:每个点拆成两个点x、x'(还是拆点╮(╯▽╰)╭),然后建一条边x→x',容量为1(源点和汇点为无穷大),费用为0。然后对每条边(i, j)建一条边,容量为无穷大,费用为1。那么不断增广直到费用大于k时停止增广,这是流量就是答案(还是求流╮(╯▽╰)╭)。

上代码(46MS):

 #include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std; const int MAXN = ;
const int MAXE = ;
const int INF = 0x3f3f3f3f;//can't modify int n, m, k, a, b; struct MinCostFlow {
bool vis[MAXN];
int head[MAXN], dis[MAXN], pre[MAXN];
int to[MAXE], next[MAXE], cost[MAXE], flow[MAXE];
int n, st, ed, ecnt; void init(int ss, int tt, int nn) {
memset(head, , sizeof(head));
ecnt = ;
st = ss, ed = tt, n = nn;
} void add_edge(int u, int v, int c, int f) {
to[ecnt] = v; cost[ecnt] = f; flow[ecnt] = c; next[ecnt] = head[u]; head[u] = ecnt++;
to[ecnt] = u; cost[ecnt] = -f; flow[ecnt] = ; next[ecnt] = head[v]; head[v] = ecnt++;
} bool spfa() {
memset(vis, , sizeof(vis));
memset(dis, 0x3f, sizeof(dis));
queue<int> que; que.push(st);
vis[st] = true; dis[st] = ;
while(!que.empty()) {
int u = que.front(); que.pop();
vis[u] = false;
for(int p = head[u]; p; p = next[p]) {
int v = to[p];
if(flow[p] && dis[v] > dis[u] + cost[p]) {
dis[v] = dis[u] + cost[p];
pre[v] = p;
if(!vis[v]) {
vis[v] = true;
que.push(v);
}
}
}
}
return dis[ed] <= k;
} void min_cost_flow(int &minFlow, int &fee) {
minFlow = fee = ;
while(spfa()) {
fee += dis[ed];
int u = ed, tmp = INF;
while(u != st) {
tmp = min(tmp, flow[pre[u]]);
u = to[pre[u] ^ ];
}
u = ed;
while(u != st) {
flow[pre[u]] -= tmp;
flow[pre[u] ^ ] += tmp;
u = to[pre[u] ^ ];
}
minFlow += tmp;
}
} int mincost() {
int ret, tmp;
min_cost_flow(tmp, ret);
return ret;
} int maxflow() {
int ret, tmp;
min_cost_flow(ret, tmp);
return ret;
}
} G; int main() {
while(scanf("%d%d%d", &n, &m, &k) != EOF) {
if(n == && m == && k == ) break;
G.init(, n * , n * );
G.add_edge(, + n, INF, );
G.add_edge(n, n + n, INF, );
for(int i = ; i < n; ++i) G.add_edge(i, i + n, , );
while(m--) {
scanf("%d%d", &a, &b);
G.add_edge(a + n, b, INF, );
}
printf("%d\n", G.maxflow());
}
}

10 11 5

1 2
2 3
3 4
4 5
5 10
2 9
1 6
6 7
7 8
8 9
9 10

然后上面的代码就过不了这组数据o(╯□╰)o,代码输出1,正确输出为2,怪不得我想不明白为什么是对的o(╯□╰)o(这数据敢不敢再弱一点……)

最后我们只能再退而求其次了o(╯□╰)o,暴力枚举答案,然后枚举最短路径上的点,深搜,再枚举删点后最短路径上的点,再深搜……

搜索(484MS,慢了点但起码是对了╮(╯▽╰)╭):

 #include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std; const int MAXN = ;
const int MAXE = ;
const int INF = 0x3fff3fff; int n, m, k, a, b, ans, ecnt;
int SP[MAXN][MAXN];
int head[MAXN], dis[MAXN], pre[MAXN];
int to[MAXE], next[MAXE];
bool del[MAXN]; void init() {
memset(head, , sizeof(head));
memset(del, , sizeof(del));
ecnt = ;
} void add_edge(int u, int v) {
to[ecnt] = v; next[ecnt] = head[u]; head[u] = ecnt++;
} bool bfs() {
memset(dis, 0x3f, sizeof(head));
queue<int> que; que.push();
dis[] = ;
while(!que.empty()) {
int u = que.front(); que.pop();
for(int p = head[u]; p; p = next[p]) {
int v = to[p];
if(!del[v] && dis[v] > n) {
dis[v] = dis[u] + ;
pre[v] = u;
if(v == n) return dis[n] <= k;
que.push(v);
}
}
}
return false;
} bool flag; void dfs(int dep) {
if(!bfs()) {
flag = true;
return ;
}
if(dep > ans) return ;
int u = pre[n], cnt = ;
while(u != ) {
SP[dep][cnt++] = u;
u = pre[u];
}
for(int i = cnt - ; i >= ; --i) {
del[SP[dep][i]] = true;
dfs(dep + );
del[SP[dep][i]] = false;
}
} int main() {
while(scanf("%d%d%d", &n, &m, &k) != EOF) {
if(n == && m == && k == ) break;
init();
while(m--) {
scanf("%d%d", &a, &b);
add_edge(a, b);
}
flag = false;
for(ans = ; ans < n; ++ans) {
dfs();
if(flag) break;
}
printf("%d\n", ans);
}
}

HDU 2485 Destroying the bus stations(!最大流∩!费用流∩搜索)的更多相关文章

  1. 图论--网络流--最小割 HDU 2485 Destroying the bus stations(最短路+限流建图)

    Problem Description Gabiluso is one of the greatest spies in his country. Now he's trying to complet ...

  2. HDU 2485 Destroying the bus stations(费用流)

    http://acm.hdu.edu.cn/showproblem.php?pid=2485 题意: 现在要从起点1到终点n,途中有多个车站,每经过一个车站为1时间,现在要在k时间内到达终点,问至少要 ...

  3. hdu 2485 Destroying the bus stations 最小费用最大流

    题意: 最少需要几个点才能使得有向图中1->n的距离大于k. 分析: 删除某一点的以后,与它相连的所有边都不存在了,相当于点的容量为1.但是在网络流中我们只能直接限制边的容量.所以需要拆点来完成 ...

  4. HDU 2485 Destroying the bus stations (IDA*+ BFS)

    传送门:http://acm.hdu.edu.cn/showproblem.php?pid=2485 题意:给你n个点,m条相连的边,问你最少去掉几个点使从1到n最小路径>=k,其中不能去掉1, ...

  5. HDU 2485 Destroying the bus stations

    2015 ACM / ICPC 北京站 热身赛 C题 #include<cstdio> #include<cstring> #include<cmath> #inc ...

  6. HDUOJ----2485 Destroying the bus stations(2008北京现场赛A题)

    Destroying the bus stations                                                                          ...

  7. Destroying the bus stations

    Destroying the bus stations Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 1832   Acce ...

  8. 【BZOJ】1834: [ZJOI2010]network 网络扩容(最大流+费用流)

    http://www.lydsy.com/JudgeOnline/problem.php?id=1834 我又思考人生了T_T,nd的数组开小了,一直wa,调了一个小时才发现啊!!!!!我一直以为我的 ...

  9. 2018.10.13 bzoj1834: [ZJOI2010]network 网络扩容(最大流+费用流)

    传送门 网络流水题啊. 第一问直接放心跑最大流(本来还以为有什么tricktricktrick). 第二问就直接把原来的边(u,v,c,w)(u,v,c,w)(u,v,c,w)变成(u,v,c,0)( ...

随机推荐

  1. Java九阳真经论述及愿景

    Java九阳真经论述及愿景 “他强由他强,清风拂山冈,他横由他横,明月照大江.” <倚天屠龙记>中张无忌被玄冥二老的玄冥神掌打伤后,体寒难耐,到处求解决之法.一次被韦蝠王打下山谷后,偶遇一 ...

  2. 搭建Extjs框架(二)

    搭建Extjs 框架 二.编写入口文件 app.js,配置extjs 组件\视图文件路径 并将app.js引入index.html       在app.js中指定一些文件的路径,Extjs页面的起始 ...

  3. jquery优化

    选择器优化执行的速度 选择器 优先:id>元素>类 使用对象缓存:即使用变量来保存对象名,var $myDiv = $("#myDiv"):$myDiv.show(); ...

  4. Ajax的open()方法

    Ajax的open()方法有3个参数:1.method:2.url:3.boolean: 参数1有get和post两个取值 参数2表示什么就不用说了 重点说下第3个参数:boolean的取值 当该bo ...

  5. 【转载】jquery实现勾选复选框触发事件给input赋值+回显复选框

    引用:https://blog.csdn.net/rui276933335/article/details/45717461 JSP: <td class="as1"> ...

  6. CentOS7 LNMP+phpmyadmin环境搭建(一、虚拟机及centos7安装)

    前一阵子配公司的服务器的时候,发现网上好多教程杂乱无章,然后便根据网上已有资料自己整理了一个lnmp环境的安装教程.因为懒,已经好久没写过博客了.趁着这次公司招新人,把之前整理的文档又整理了一次,顺便 ...

  7. 基于Geomesa服务查询轨迹数据无法根据空间和时间范围进行结果查询

    一.Geomesa - QuickStart(教程工程包)   百度网盘下载地址:geomesa-tutorials-master.7z 二.解压后,IDEA编译如下 百度网盘下载地址:IDEA201 ...

  8. 如何将24位RGB颜色转换16位RGB颜色

    有许多朋友第一次使用16位彩色显示屏会遇到如何将24位RGB颜色转换为对应的16位RGB颜色的问题, 通过查阅相关资料,就写一下其中的转换原理吧,希望对大家会有所帮助. 我们知道24位RGB是分别由8 ...

  9. Python学习手册之 Python 之禅、Python 编程规范和函数参数

    在上一篇文章中,我们介绍了 Python 的正则表达式使用示例,现在我们介绍 Python 之禅. Python 编程规范和函数参数.查看上一篇文章请点击:https://www.cnblogs.co ...

  10. 部署zabbix,自动发现lnmp环境,监控主机状态,实现 邮件及微信报警(配置server端)

    二.配置server端监控 1.监控apache 首先在本机下载模板:https://github.com/rdvn/zabbix-templates/archive/master.zip  该 zi ...