秘密任务

秘密任务

Description

在这里插入图片描述

Input

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

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

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

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

Output

输出 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。无向图可能 有重边 。

题目大意

给你一个图,要求求出其最短路图中的最小割,并且判断方案是否唯一。

题解

这道题真的好麻烦…

首先我们要求出最短路图(就是跑一遍最短路求出dis,然后再看每条边是否是最短路上的即可)。

然后我们设f[i][j]表示i到j有几条边在最短路上,那么我们就可以重新建出一张图。

建图方法如下:

1.将一条边之间增加一个点:

x————————>y ==> x ————>t————>y

这个点t是新建的一个节点。

2.将x到t的边权设为 f [ i ] [ j ] ∗ a [ i ] f[i][j]*a[i] f[i][j]a[i],将t到y的边权设为 f [ i ] [ j ] ∗ a [ i ] f[i][j]*a[i] f[i][j]a[i],然后.就可以啦

3.注意特判如果y是n,那么要将t到y的边权设为INF(不能被割掉)。

然后我们就可以愉快的在上面跑最小割(最大流)啦!

跑出来的就是第二问的答案。

那么我们怎么求第一问的答案呢?

显然,如果我们每条必割边的边权之和等于最小割,那么我们最小割集里面每条边都是必割边,那么答案显然唯一(就是Yes)。

反之则为No。

我们可以在残量网络中整理出与S(源点)相连的集合和与T(汇点)相连的集合,那么如果一条边的两个端点分别属于S集合和T集合,那么我们就可以认为这是必割边。

证明显然。

这题细节好多…

code

#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#define N 1005
#define M 60005
#define ll long long
#define mem(x,y) memset(x,y,sizeof x)
using namespace std;
//For 2000 yuan
int T,n,m,a[N],tot,jl[N][N],r[N][N],bz[M],f[N][N],w[M*2],h,t,tov[M*2],last[M],next[M*2],len[M*2],tot1,X[M],Y[M];
ll nn,ans,pre[M],c[M],flow[M],dis[M];
int ins1(int x,int y,int z){jl[x][++jl[x][0]]=y,r[x][jl[x][0]]=z,jl[y][++jl[y][0]]=x,r[y][jl[y][0]]=z;}
int ins(int x,int y,int z){tov[++tot1]=y,next[tot1]=last[x],len[tot1]=z,last[x]=tot1,tov[++tot1]=x,next[tot1]=last[y],len[tot1]=0,last[y]=tot1;}
ll bfs(int x,int y)
{
	mem(pre,0),mem(c,0),mem(w,0),mem(flow,0),w[1]=x,pre[x]=x,flow[y]=0,flow[x]=1e9;
	int h=0,t=1;
	while (h<t)
	{
		int z=w[++h];
		if (z==y)
			break;
		for (int i=last[z];i;i=next[i])
			if (!pre[tov[i]]&&len[i])
				w[++t]=tov[i],pre[tov[i]]=z,flow[tov[i]]=min(flow[z],(ll)len[i]),c[tov[i]]=i;
	}
	return flow[y];
} 
ll getans(int x,int y)
{
	ll ans=0,t=bfs(x,y);
	while (t)
	{
		for (int i=y;i!=x;i=pre[i])
			len[c[i]]-=t,len[c[i]^1]+=t;
		ans+=t,t=bfs(x,y);
	}
	return ans;
}
int main()
{
	scanf("%d",&T);
	while (T--)
	{
		scanf("%d%d",&n,&m),tot1=1,nn=n,mem(last,0),mem(bz,0),mem(w,0),mem(jl,0),mem(f,0),mem(X,0),mem(Y,0);
		for (int i=1;i<n;i++)
			scanf("%d",&a[i]);
		for (int i=1,z,y,x;i<=m;i++)
			scanf("%d%d%d",&x,&y,&z),ins1(x,y,z);
		mem(dis,63),h=0,w[t=1]=1,bz[1]=1,dis[1]=0;
		while (h<t)
		{
			int x=w[++h];
			for (int i=1;i<=jl[x][0];i++)
				if (dis[x]+r[x][i]<dis[jl[x][i]])
				{
					dis[jl[x][i]]=r[x][i]+dis[x];
					if (!bz[jl[x][i]])
						w[++t]=jl[x][i],bz[jl[x][i]]=1;
				}
			bz[x]=0;
		}
		for (int i=1;i<=n;i++)
			for (int j=1;j<=jl[i][0];j++)
				if (dis[i]+r[i][j]==dis[jl[i][j]])
					f[i][jl[i][j]]++;
		for (int i=1;i<=n;i++)
			for (int j=1;j<=n;j++)
				if (f[i][j])
					nn++,ins(i,nn,(i==n)?1e9:f[i][j]*a[i]),ins(nn,j,(j==n)?1e9:f[i][j]*a[j]);
		ans=getans(1,n);
		mem(w,0),h=0,t=1,w[1]=1,X[++X[0]]=1,mem(bz,0),bz[1]=1;
		while (h<t)
		{
			int x=w[++h];
			for (int i=last[x];i;i=next[i])
				if (len[i]&&!bz[tov[i]])
					w[++t]=tov[i],X[++X[0]]=tov[i],bz[tov[i]]=1;
		}
		mem(w,0),h=0,t=1,w[1]=n,mem(bz,0),bz[n]=1,Y[++Y[0]]=n;
		while (h<t)
		{
			int x=w[++h];
			for (int i=last[x];i;i=next[i])
				if (len[i^1]&&!bz[tov[i]])
					w[++t]=tov[i],Y[++Y[0]]=tov[i],bz[tov[i]]=1;
		}
		mem(bz,0);
		for (int i=1;i<=Y[0];i++)
			bz[Y[i]]=1;
		ll sum=0;
		for (int ii=1,x;ii<=X[0];ii++)
		{
			x=X[ii];
			for (int i=last[x];i;i=next[i])
				if (len[i]==0&&bz[tov[i]])
					sum+=len[i^1];
		}
		(sum==ans)?printf("Yes "):printf("No ");
		printf("%d\n",ans);
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值