poj 3921/hdu 2485 Destroying the bus stations(最短路而非网络流费用流)

题意:给出一张有向图,每条边长度均为1,问至少删掉几个点,使得从1到n的最短路径长度大于k(长度小于等于k的不相交路径数目)

第一种可以AC的做法:

先求一次最短路,对于d[1][i]+d[i][n]>k的点一定是不需要去除的,然后对于剩下的点,求至少删掉多少个点使其不连通,典型的有向图点连通度问题,拆点求最小割即可,但是过不了这组数据:

8 10 5
1 2
2 3
3 4
4 5
5 6
6 8
1 7
7 8
4 7
7 4
画个图明显知道错在哪了,然后就有了第二张可以AC的做法:

把每个点拆成两个点,a1和a2,中间连一条容量为1,费用为0的边,对于每一条数据中的有向边a-->b,连一条a2-->b1的容量为INF,费用为1的边

剩下的就是跑费用流啦,当最短路大于K停止,但这样同样无法求得不相交路径数目。比如如下数据:

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
于是有了第三种做法,最朴素的做法,但是复杂度不是很好估计,应该是显然正确的:
dfs搜索,每层求最短路,依次枚举删除最短路径的上的点,然后进入下一层,直到最短路大于k,更新答案(bfs是错误的,因为bfs相当于费用流做法)
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;
import java.util.Arrays;

public class Main {
	class SPFA {
		int maxn = 110, maxm = 5010;
		int inf = 1 << 30;
		class node {
			int be, ne;
			int val;
			node(int b, int e, int v) {
				be = b;
				ne = e;
				val = v;
			}
		}
		node buf[] = new node[maxm];
		int len, E[] = new int[maxn], n, queue[] = new int[maxn];
		int d[] = new int[maxn];
		void init(int n) {
			this.n = n;
			Arrays.fill(E, -1);
			len = 0;
		}
		void add(int a, int b, int v) {
			if (a == b)
				return;
			buf[len] = new node(b, E[a], v);
			E[a] = len++;
		}
		int pre[] = new int[maxn];
		boolean vis[] = new boolean[maxn];
		int solve(int s, int t) {
			int head = 0, tail = 0;
			Arrays.fill(vis, false);
			Arrays.fill(d, inf);
			Arrays.fill(pre, -1);
			d[s] = 0;
			queue[(tail++) % maxn] = s;
			vis[s] = true;
			int a, b;
			while (head != tail) {
				a = queue[(head++) % maxn];
				vis[a] = false;
				for (int i = E[a]; i != -1; i = buf[i].ne) {
					b = buf[i].be;
					if (d[a] + buf[i].val < d[b]) {
						d[b] = d[a] + buf[i].val;
						pre[b] = a;
						if (!vis[b]) {
							vis[b] = true;
							queue[(tail++) % maxn] = b;
						}
					}
				}
			}
			return d[t];
		}
	}

	SPFA sp = new SPFA();
	int ans, n, m, k;
	void dfs(int cnt) {
		int temp = sp.solve(1, n);
		if (temp > k) {
			ans = Math.min(ans, cnt);
			return;
		}
		if(cnt+1>=ans)
			return;
		int stack[]=new int[n+1],top=0;
		int v=sp.pre[n];
		while(v!=1){
			stack[top++]=v;
			v=sp.pre[v];
		}
		for(int i=0;i<top;i++){
			temp=sp.E[stack[i]];
			sp.E[stack[i]]=-1;
			dfs(cnt+1);
			sp.E[stack[i]]=temp;
		}
	}

	void run() throws IOException {
		while (true) {
			n = nextInt();
			m = nextInt();
			k = nextInt();
			if (n == 0)
				break;
			sp.init(n);
			while (m-- > 0)
				sp.add(nextInt(), nextInt(), 1);
			ans=n;
			dfs(0);
			System.out.println(ans);
		}
	}
	StreamTokenizer in = new StreamTokenizer(new BufferedReader(
			new InputStreamReader(System.in)));
	int nextInt() throws IOException {
		in.nextToken();
		return (int) in.nval;
	}
	public static void main(String[] args) throws IOException {
		new Main().run();
	}

}

ps:现场赛竟然也有这样不负责的题目。。。。真令人胆寒


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值