USTC 1280 / 携程决赛1004 最短路径的代价

最短路径的代价

Time Limit : 2000/1000ms (Java/Other)   Memory Limit : 32768/32768K (Java/Other)
Total Submission(s) : 32   Accepted Submission(s) : 15
Font: Times New Roman | Verdana | Georgia
Font Size:  

Problem Description

通常情况下找到N个节点、M 条边的无向图中某两点的最短路径很简单。假设每条边都有各自的长度以及删除这条边的代价,那么给出两个点X和Y,删除一些边可以使得X到Y的最短路径增加,求删除边最少的代价。

Input

第一行包含参数N和M,分别表示节点和边的个数 (2 ≤N≤ 1000,1 ≤M≤ 11000 )
第二行包含参数X和Y,表示源点和终点( 1 ≤ X,Y ≤ N,X ≠ Y);
剩下M行每行包含4个参数Ui、Vi、Wi和Ci,分别表示节点Ui和Vi之间边的长度Wi以及删除的代价Ci ( 1 ≤ Ui , Vi ≤ N, 0 ≤Wi, Ci ≤1000 );
如果某行N=M=0就表示输入结束。

Output

对于每个用例,按行输出增加X和Y直接最短路径所需要的最小代价。这个代价是所有删除的边的代价之和。

Sample Input

2 3
1 2
1 2 2 3
1 2 2 4
1 2 3 5
4 5
1 4
1 2 1 1
2 3 1 1
3 4 1 1
1 4 3 2
2 2 2 3
4 5
2 3
1 2 3 2
2 4 3 4
1 3 2 3
3 4 2 3
1 4 0 1
0 0

Sample Output

7
3
6

Source

CodingTrip - 携程编程大赛 (决赛)
LINK: http://acm.ustc.edu.cn/ustcoj/problem.php?id=1280
这个比赛好多原题,无论是资格赛还是决赛,不吐槽了。  可惜我没有见过几道啊。
这道题目是说, 给你一个图,从s到t 有最短路,图上的每条边都有长度和 删去的代价。要求删除一些边,使得由s->t的最短路增大。要求删掉的边的代价最小。
解法: 先求s->t的最短路。建立新图,如果dis[u]==dis[v]+ 由v到u的边的长度,则添加边(v,u),边长为删去的代价.
之后就是求一个最小割/最大流,把s,t分开.

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#include<vector>
#include<queue>
#include<cmath>
using namespace std;
#define INF 1000000000
#define N 1005
#define M 100005
int n,m,s,t,hh1[N],hh2[N],tot1,tot2;
int dis[N],inq[N],vis[N],lev[N];
struct node
{
	int u,v,w,c,next;
}edge1[2*M],edge2[2*M];
void init()
{
	tot1=tot2=0;
	memset(hh1,-1,sizeof(hh1));
	memset(hh2,-1,sizeof(hh2));
}
void add1(int u,int v,int w,int c)
{
	edge1[tot1].u=u; edge1[tot1].v=v;
	edge1[tot1].w=w; edge1[tot1].c=c;
	edge1[tot1].next=hh1[u]; hh1[u]=tot1++;
}
void add2(int u,int v,int c)
{
	edge2[tot2].u=u; edge2[tot2].v=v;
	edge2[tot2].w=c; edge2[tot2].next=hh2[u];
	hh2[u]=tot2++;
	edge2[tot2].u=v; edge2[tot2].v=u;
	edge2[tot2].w=0; edge2[tot2].next=hh2[v];
	hh2[v]=tot2++;
}
void spfa()
{
	queue<int > Q;
	Q.push(s);
	memset(inq,0,sizeof(inq));
	inq[s]=1;
	for(int i=0;i<=n;i++) dis[i]=INF;
	dis[s]=0;
	while(!Q.empty())
	{
		int u=Q.front(); Q.pop();
		inq[u]--;
		for(int i=hh1[u];i!=-1;i=edge1[i].next)
		{
			int v=edge1[i].v;
			if(dis[v]>dis[u]+edge1[i].w)
			{
				dis[v]=dis[u]+edge1[i].w;
				if(!inq[v])
				{
					inq[v]=1;
					Q.push(v);
				}
			}
		}
	}

}
void add_()
{
	for(int u=1;u<=n;u++)
	{
		for(int j=hh1[u];j!=-1;j=edge1[j].next )
		{
			int v=edge1[j].v;
			if(dis[v]==dis[u]+edge1[j].w) add2(u,v,edge1[j].c);
		}
	}
}
int bfs()
{
	queue<int > Q;
	memset(lev,-1,sizeof(lev));
	lev[s]=1; Q.push(s);
	while(!Q.empty())
	{
		int u=Q.front(); Q.pop();
		for(int i=hh2[u];i!=-1; i=edge2[i].next )
		{
			int v=edge2[i].v;
			if(edge2[i].w && lev[v]==-1)
			{
				lev[v]=lev[u]+1;
				Q.push(v);
			}
		}
	}
	return lev[t]!=-1;
}
int dfs(int u,int flow)
{
	if(u==t) return flow;
	int tmp=flow;
	for(int i=hh2[u];i!=-1; i=edge2[i].next )
	{
		int v=edge2[i].v;
		if(lev[v]==lev[u]+1)
		{
			int ad=dfs(v,min(tmp,edge2[i].w));
			tmp-=ad;
			edge2[i].w-=ad;
			edge2[i^1].w+=ad;
			if(!tmp) break;
		}
	}
	return flow-tmp;
}
int dinic()
{
	int ret=0;
	while(bfs())
	{
		ret+=dfs(s,INF);
	}
	return ret;
}
int main()
{
	//freopen("in.txt","r",stdin);
	int time=0;
	while(scanf("%d%d",&n,&m),n||m)
	{
		scanf("%d%d",&s,&t);
		init();
		int u,v,w,c;
		for(int i=1;i<=m;i++)
		{
			scanf("%d%d%d%d",&u,&v,&w,&c);
			add1(u,v,w,c);
			add1(v,u,w,c);
		}
		spfa();
		memset(vis,0,sizeof(vis));
		add_();
		int ans=dinic();
		printf("Case %d: %d\n",++time,ans);
		//printf("%d\n",ans);
	}
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值