题意:给出一张有向图,每条边长度均为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:现场赛竟然也有这样不负责的题目。。。。真令人胆寒