您的位置:首页 > 其它

[BZOJ 1797][BZOJ 3258]最小割的唯一性判定

2016-03-14 21:00 399 查看
问题一:是否存在一个最小代价路径切断方案,其中该道路被切断? 问题二:是否对任何一个最小代价路径切断方案,都有该道路被切断? 现在请你回答这两个问题。

最小割唯一性判定

jcvb:

在残余网络上跑tarjan求出所有SCC,记id[u]为点u所在SCC的编号。显然有id[s]!=id[t](否则s到t有通路,能继续增广)。

①对于任意一条满流边(u,v),(u,v)能够出现在某个最小割集中,当且仅当id[u]!=id[v];

②对于任意一条满流边(u,v),(u,v)必定出现在最小割集中,当且仅当id[u]==id[s]且id[v]==id[t]。



<==将每个SCC缩成一个点,得到的新图就只含有满流边了。那么新图的任一s-t割都对应原图的某个最小割,从中任取一个把id[u]和id[v]割开的割即可证明。



<==:假设将(u,v)的边权增大,那么残余网络中会出现s->u->v->t的通路,从而能继续增广,于是最大流流量(也就是最小割容量)会增大。这即说明(u,v)是最小割集中必须出现的边。

#include
#include
#include
#include
#include
#include
#define maxn 200000
using namespace std;

const int inf = 0x7fffffff;

struct Edge{
int to, next, w;
}edge[maxn];

int from[maxn], to[maxn];

int h[maxn], cnt = 1;

void add(int u, int v, int w){
cnt ++;
edge[cnt].to = v;
edge[cnt].next = h[u];
edge[cnt].w = w;
h[u] = cnt;
swap(u, v);w = 0;
cnt ++;
edge[cnt].to = v;
edge[cnt].next = h[u];
edge[cnt].w = w;
h[u] = cnt;
}

int n, m, S, T;

queueQ;

int d[4010];

bool BFS(){
Q.push(S);
memset(d, -1, sizeof d);
d[S] = 0;
while(!Q.empty()){
int u = Q.front();Q.pop();
for(int i = h[u]; i; i = edge[i].next){
if(!edge[i].w)continue;
int v = edge[i].to;
if(d[v] == -1){
d[v] = d[u] + 1;
Q.push(v);
}
}
}return d[T] != -1;
}

int DFS(int x, int a){
if(a == 0 || x == T)return a;
int used = 0, f;
for(int i = h[x]; i; i = edge[i].next){
int v = edge[i].to;
if(d[v] == d[x] + 1){
f = DFS(v, min(edge[i].w, a - used));
edge[i].w -= f;
edge[i ^ 1].w += f;
used += f;
if(used == a)return used;
}
}
if(!used)d[x] = -1;
return used;
}

void Dinic(){
int ans = 0;
while(BFS())
ans += DFS(S, inf);
}

int low[maxn], pre[maxn], dfs_clock, sccno[maxn], scc_cnt;

stacks;

void Tarjan(int u){
low[u] = pre[u] = ++ dfs_clock;
s.push(u);
for(int i = h[u]; i; i = edge[i].next){
if(!edge[i].w)continue;
int v = edge[i].to;
if(!pre[v])Tarjan(v), low[u] = min(low[u], low[v]);
else if(!sccno[v])low[u] = min(low[u], pre[v]);
}
if(low[u] == pre[u]){
++ scc_cnt;
for(;;){
int v = s.top();s.pop();
sccno[v] = scc_cnt;
if(v == u)break;
}
}
}

int main(){
freopen("mincut.in" ,"r",stdin );
freopen("mincut.out","w",stdout);
scanf("%d%d%d%d", &n, &m, &S, &T);
int w;
for(int i = 1; i <= m; i ++){
scanf("%d%d%d", &from[i], &to[i], &w);
add(from[i], to[i], w);
}
Dinic();
for(int i = 1; i <= n; i ++)
if(!pre[i])Tarjan(i);
for(int i = 1; i <= m; i ++){
int u = from[i], v = to[i];
if(edge[i << 1].w){
printf("0 0\n");
continue;
}
if(sccno[u] == sccno[v])
printf("0 0\n");
else if(sccno[u] == sccno[S] && sccno[v] == sccno[T])
printf("1 1\n");
else printf("1 0\n");
}
return 0;
}


3258: 秘密任务

Description

Alice听说在一片神奇的大陆MagicLand,有一个古老的传说…… 

 很久很久以前,那个时候 MagicStates共和国刚刚成立。 反对新政府的势力虽已被镇压,但仍然在暗地活动。这一次,情报局得到了一个令人震惊的消息,被软禁在首都府邸中的Frank ——著名的反对派领袖,秘密逃出首都,去往反对派的大本营。根据相关的情报,Frank计划通过城市之间 发达的高速公路,经过最短的路程抵达目的地。不妨将 MagicStates共和国简化为由N个城市,M条高速公路构成的连通的无向图,首都为城市1,反对派的大本营为城市N。 

每条高速公路连接两个不同的城市,且路程是已知的。而Frank选择了一条从城市1到城市N的最短路径作为他的逃跑路线。为了阻止Frank,共和国总统决定在某些城市的高速公路的出入口设立检查 点,在Frank经过检查点时将他逮捕。 

举例来说,如果有一条高速公路连接城市u和城市v,在这条公路的城市u或城市v的出入口设立检查点,那么Frank经过高速公路时就会被发现。特别的是,由于城市N实际上处在反对派的控制下,所以不能在城市N设立检查点。

然而在任何城市设立检查点都需要一定的费用。更具体的,若在城市 u设立k个检查点,就要花费 Au乘以k的代价,其中Au是城市u的相关参数。值得注意的是,这个代价与这k个检查点具体设在哪些公路的出入口无关,于是,总统责令情报局拟定一个方案,花费最小的代价使得无论Frank选择哪条最短路线,都会在(除城市N以外)某个城市的高速公路出入口被发现。读到这里,Alice很想知道阻止Frank所需要花费的最小代价,并且她还希 望知道最优方案是否是唯一的。只好再请你帮助她了。 

注意,我们称两个方案不同当且仅当存在某城市k,两种方案中在城市 k的检查点的设置(而不仅是数目)是不同的。 

注意,输入文件包含多组测试数据。

BZOJ3258秘密任务
最小割唯一性判定

这才是真正的最小割唯一性判定
注意一个二元组不能随便建。
要解方程。
此题要拆边,对应两条边两个点的属性
不要任性T^T

#include
#include
#include
#include
#include
#include
#define maxn 10010
using namespace std;

typedef long long ll;

int n, m, S, T, size;
struct Edge{int to, next, w;}edge[100010];

int h[maxn], cnt, val[maxn], fr[100010], to[100010], C;
void add(int u, int v, int w){
C ++, fr[C] = u, to[C] = v;
cnt ++;
edge[cnt].to = v;
edge[cnt].next = h[u];
edge[cnt].w = w;
h[u] = cnt;
swap(u, v);w = 0;
cnt ++;
edge[cnt].to = v;
edge[cnt].next = h[u];
edge[cnt].w = w;
h[u] = cnt;
}
queueQ;
namespace spfa{
struct Edge_{int to, next, dis;}G[100010];
int cnt, h[maxn];
ll dis[maxn], disT[maxn];
bool vis[maxn];
void addG(int u, int v, int d){
cnt ++;
G[cnt].to = v;
G[cnt].next = h[u];
G[cnt].dis = d;
h[u] = cnt;
}
void work(){
cnt = 0;
memset(h, 0, sizeof h);
int u, v, d;
for(int i = 1; i <= m; i ++){
scanf("%d%d%d", &u, &v, &d);
addG(u, v, d);
addG(v, u, d);
}
memset(dis, 0x7f, sizeof dis);
memset(vis, 0, sizeof vis);
dis[S] = 0;Q.push(S);
while(!Q.empty()){
int u = Q.front();Q.pop();
for(int i = h[u]; i; i = G[i].next){
int v = G[i].to;
if(dis[v] > dis[u] + G[i].dis){
dis[v] = dis[u] + G[i].dis;
if(!vis[v])vis[v] = true, Q.push(v);
}
}
vis[u] = false;
}
memset(disT, 0x7f, sizeof disT);
memset(vis, 0, sizeof vis);
disT[T] = 0;Q.push(T);
while(!Q.empty()){
int u = Q.front();Q.pop();
for(int i = h[u]; i; i = G[i].next){
int v = G[i].to;
if(disT[v] > disT[u] + G[i].dis){
disT[v] = disT[u] + G[i].dis;
if(!vis[v])vis[v] = true, Q.push(v);
}
}
vis[u] = false;
}
size = n;
for(int i = 1; i < n; i ++)
for(int j = h[i]; j; j = G[j].next){
int v = G[j].to;
if(dis[T] == dis[i] + G[j].dis + disT[v]){
++ size;
add(i, size, val[i]);
add(size, v, val[v]);
//add(i, v, min(val[i], val[v]));
}
}
}
}

const int inf = 0x7fffffff;

int d[maxn];
bool BFS(){
memset(d, -1, sizeof d);
d[S] = 0;
Q.push(S);
while(!Q.empty()){
int u = Q.front();Q.pop();
for(int i = h[u]; i; i = edge[i].next){
if(!edge[i].w)continue;
int v = edge[i].to;
if(d[v] == -1){
d[v] = d[u] + 1;
Q.push(v);
}
}
}return d[T] != -1;
}

ll DFS(int x, ll a){
if(a == 0 || x == T)return a;
ll used = 0, f;
for(int i = h[x]; i; i = edge[i].next){
int v = edge[i].to;
if(d[v] == d[x] + 1){
f = DFS(v, min(a - used, (ll)edge[i].w));
edge[i].w -= f;
edge[i ^ 1].w += f;
used += f;
if(used == a)return used;
}
}
if(!used)d[x] = -1;
return used;
}

ll Dinic(){
ll ans = 0;
while(BFS())
ans += DFS(S, inf);
return ans;
}

int sccno[maxn], pre[maxn], low[maxn], dfs_clock, scc_cnt;
stacks;
void Tarjan(int u){
pre[u] = low[u] = ++ dfs_clock;
s.push(u);
for(int i = h[u]; i; i = edge[i].next){
if(!edge[i].w)continue;
int v = edge[i].to;
if(!pre[v])Tarjan(v), low[u] = min(low[u], low[v]);
else if(!sccno[v])low[u] = min(low[u], pre[v]);
}
if(low[u] == pre[u]){
++ scc_cnt;
for(;;){
int v = s.top();s.pop();
sccno[v] = scc_cnt;
if(v == u)break;
}
}
}

int main(){
//freopen("secret.in", "r", stdin);
//freopen("secret.out", "w", stdout);
int test;
scanf("%d", &test);
while(test --){
C = 0;
scanf("%d%d", &n, &m);
for(int i = 1; i < n; i ++)
scanf("%d", &val[i]);
val
= 0x7fffffff;
memset(h, 0, sizeof h);
cnt = 1;
S = 1, T = n;
spfa::work();
ll ans = Dinic();

memset(pre, 0, sizeof pre);
memset(low, 0, sizeof low);
memset(sccno, 0, sizeof sccno);
scc_cnt = dfs_clock = 0;

for(int i = 1; i <= size; i ++)
if(!pre[i])Tarjan(i);
for(int i = 1; i <= C; i ++){
int u = fr[i], v = to[i];
if(edge[i << 1].w || sccno[u] == sccno[v])continue;
if(sccno[u] == sccno[S] && sccno[v] == sccno[T])
continue;
printf("No %lld\n", ans);
goto Mark;
}
printf("Yes %lld\n", ans);
Mark:;
}

return 0;
}


重点是看S和T是否联通

我们的思想是Tarjan找桥边。

如果存在可能不是最小割上的边

那么就不唯一。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  最小割