[最小割]JZOJ 3348 秘密任务

Description

 

Input

输入文件为secret.in :

第一行 包含一 个正整数 T,表示有 T组测试数据。接下来 依次是 T组测试数 据。

每组测试数 据的第一行包含两个整N、M。

第二行包含 N - 1个正整数,依次表示 A1,A2, …,AN-1。

接下来 M行,每 行为三个 整数: ui、vi、ci,表示一条连接城市ui和城市 vi的路程等于 ci的高速公路。

 

Output

输出文件为 secret.out 。

输出 T行, 依次 表示 每组测试数据 的答案 。若最优 方案 唯一 则输出 ”Yes”和 最小 代价 ,否则 输出 ”No ”和最小 代价 。字符串 和整数 之间 请用一个 空格 隔开 。
 

Sample Input

3
3 3
2 4
1 3 23
3 2 12
2 1 11
4 4
3 2 2
1 2 1
2 3 1
3 4 1
4 1 1
3 4
3 2
1 2 1
2 3 2
2 3 19
3 1 4

Sample Output

Yes 4
Yes 3
No 2

【样例解释】
第 1组测试数据: 最优 方案 是在城市 1设立 两个 检查点 。
第 2组测试数据: 最优 方案 是城市 1的高速公路 (1, 4 )的出入口设立 检查点。
第 3组测试数据: 最优 方案 是在城市 2设立 一个 检查点 ,不过 既可以 设置 在 高速公路 (1, 2)的出入 口,也可以 设置 在高速公路 (2, 3)的出入口 。
 
 

Data Constraint

对于 10% 的数据: 2 ≤ N ≤ 10 , 1 ≤ M ≤ 20。

另有 40% 的数据: 最优 方案 是唯一 的。

对于 10 0% 的数据: 2 ≤ N ≤ 400, 1 ≤ M ≤ 4 00 0,1 ≤ T ≤ 5,1 ≤ Ai, c ≤ 10^9。无向图可能 有重边 。

分析

建立在最短路条件上的题目,自然要先把最短路从图里面抠出来

然后可以发现是个最小割……由于一条边的拦截选择有两种,拆边即可

然后最难的是方案判断,在残量网络上比较不显然的一个结论是:

从s出发可到达的点集为S,可到达t的点集为T

如果一个边是最小割的一部分,当且仅当它的端点不完全属于一个集合并且它的出发一端不属于S或到达端不属于T,则方案不唯一

 

#include <iostream>
#include <cstdio>
#include <queue>
#include <memory.h>
using namespace std;
typedef long long ll;
const int N=4e2+10;
const int M=4e3+10;
const ll Inf=1ll<<62;
struct Pipe {
    int v,nx;
    ll c;
}e[8*M];
struct Graph {
    int v,nx;
    ll w;
}g[2*M];
int cnt,list[N],lst[M+N];
ll dis[M+N],ans,a[N];
bool vis[N],p;
int T,n,m,s,t;

void Add_Pipe(int u,int v,ll c) {
    e[++cnt]=(Pipe){v,lst[u],c};lst[u]=cnt;
    e[++cnt]=(Pipe){u,lst[v],0};lst[v]=cnt;
}

void Add(int u,int v,ll w) {
    g[++cnt]=(Graph){v,list[u],w};list[u]=cnt;
    g[++cnt]=(Graph){u,list[v],w};list[v]=cnt;
}

void SPFA() {
    queue<int> q;
    while (!q.empty()) q.pop();
    for (int i=1;i<=n;i++) dis[i]=Inf;
    memset(vis,0,sizeof vis);
    q.push(1);vis[1]=1;dis[1]=0;
    while (!q.empty()) {
        int u=q.front();q.pop();
        for (int i=list[u];i;i=g[i].nx)
            if (dis[g[i].v]>dis[u]+g[i].w) {
                dis[g[i].v]=dis[u]+g[i].w;
                if (!vis[g[i].v]) q.push(g[i].v);
                vis[g[i].v]=1;
            }
        vis[u]=0;
    }
}

void BFS() {
    queue<int> q;
    while (!q.empty()) q.pop();
    memset(lst,0,sizeof lst);memset(vis,0,sizeof vis);
    q.push(n);t=n;cnt=1;
    while (!q.empty()) {
        int u=q.front();q.pop();
        for (int i=list[u];i;i=g[i].nx)
            if (dis[u]-g[i].w==dis[g[i].v]) {
                Add_Pipe(g[i].v,++t,a[g[i].v]);Add_Pipe(t,u,a[u]);
                if (!vis[g[i].v]) q.push(g[i].v);
                vis[g[i].v]=1;
            }
    }
    t=n;
}

bool BFS1() {
    queue<int> q;
    while (!q.empty()) q.pop();
    memset(dis,0,sizeof dis);
    q.push(1);dis[1]=1;
    while (!q.empty()) {
        int u=q.front();q.pop();
        for (int i=lst[u];i;i=e[i].nx)
            if (!dis[e[i].v]&&e[i].c) {
                dis[e[i].v]=dis[u]+1;
                q.push(e[i].v);
            }
    }
    return dis[t];
}

ll Max_Flow(int u,ll mf) {
    ll ret=0;
    if (mf==0||u==t) return mf;
    for (int i=lst[u];i;i=e[i].nx)
        if (dis[e[i].v]==dis[u]+1&&e[i].c) {
            ll flow=Max_Flow(e[i].v,min(mf-ret,e[i].c));
            e[i].c-=flow;e[i^1].c+=flow;
            ret+=flow;
            if (ret==mf) return ret;
    }
    return ret;
}

void DFS1(int u) {
    dis[u]=1;
    for (int i=lst[u];i;i=e[i].nx)
        if (!dis[e[i].v]&&e[i].c) DFS1(e[i].v);
}

void DFS2(int u) {
    dis[u]=2;
    for (int i=lst[u];i;i=e[i].nx)
        if (!dis[e[i].v]&&e[i^1].c) DFS2(e[i].v);
}

int main() {
    for (scanf("%d",&T);T;T--) {
        scanf("%d%d",&n,&m);
        memset(list,0,sizeof list);cnt=1;
        for (int i=1;i<n;i++) scanf("%lld",&a[i]);a[n]=Inf;
        for (int i=1,u,v;i<=m;i++) {ll w;scanf("%d%d%lld",&u,&v,&w),Add(u,v,w);}
        SPFA();BFS();s=1;ans=0;
        while (BFS1()) ans+=Max_Flow(s,Inf);
        memset(dis,0,sizeof dis);DFS1(s);DFS2(t);
        bool p=0;
        for (int i=2;i<=cnt;i+=2)
            if (!e[i].c&&dis[e[i^1].v]!=dis[e[i].v]&&(dis[e[i^1].v]!=dis[s]||dis[e[i].v]!=dis[t])) {
                printf("No %d\n",ans);
                p=1;
                break;
            }
        if (!p) printf("Yes %d\n",ans);
    }
}
View Code

 

Description

 

Input

输入文件为secret.in :

第一行 包含一 个正整数 T,表示有 T组测试数据。接下来 依次是 T组测试数 据。

每组测试数 据的第一行包含两个整N、M。

第二行包含 N - 1个正整数,依次表示 A1,A2, …,AN-1。

接下来 M行,每 行为三个 整数: ui、vi、ci,表示一条连接城市ui和城市 vi的路程等于 ci的高速公路。

 

Output

输出文件为 secret.out 。

输出 T行, 依次 表示 每组测试数据 的答案 。若最优 方案 唯一 则输出 ”Yes”和 最小 代价 ,否则 输出 ”No ”和最小 代价 。字符串 和整数 之间 请用一个 空格 隔开 。
 

Sample Input

3
3 3
2 4
1 3 23
3 2 12
2 1 11
4 4
3 2 2
1 2 1
2 3 1
3 4 1
4 1 1
3 4
3 2
1 2 1
2 3 2
2 3 19
3 1 4

Sample Output

Yes 4
Yes 3
No 2

【样例解释】
第 1组测试数据: 最优 方案 是在城市 1设立 两个 检查点 。
第 2组测试数据: 最优 方案 是城市 1的高速公路 (1, 4 )的出入口设立 检查点。
第 3组测试数据: 最优 方案 是在城市 2设立 一个 检查点 ,不过 既可以 设置 在 高速公路 (1, 2)的出入 口,也可以 设置 在高速公路 (2, 3)的出入口 。
 
 

Data Constraint

对于 10% 的数据: 2 ≤ N ≤ 10 , 1 ≤ M ≤ 20。

另有 40% 的数据: 最优 方案 是唯一 的。

对于 10 0% 的数据: 2 ≤ N ≤ 400, 1 ≤ M ≤ 4 00 0,1 ≤ T ≤ 5,1 ≤ Ai, c ≤ 10^9。无向图可能 有重边 。
posted @ 2019-07-10 21:27  Vagari  阅读(98)  评论(0编辑  收藏  举报