题目链接:bzoj1927
题目大意:
有N颗行星和M条双向星际航路。你有两种移动模式:高速航行模式和能力爆发模式。爆发模式是可以在任意时刻任意跳的。而高速航行只能从编号小的到编号大的走。问在一个路线之外的不知名行星出发,经过每个点恰好一次的最少时间。
题解:
最小费用最大流
拆点,一个表示入的一个表示出的。
- S→i ,流量为1,费用为0
- i′→T ,流量为1,费用为0
- S→i′ ,流量为1,费用为 爆发所需要的时间
- i→j′,i<j ,流量为1,费用为 高速航行所需要的时间
举个栗子吧:样例的构图如下
【。。有点丑,棕色的表示流量,灰蓝的表示费用。】
i
的出边表示经过
/**************************************************************
Problem: 1927
User: 564415605
Language: C++
Result: Accepted
Time:4256 ms
Memory:2508 kb
****************************************************************/
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<queue>
#include<iostream>
#include<algorithm>
using namespace std;
#define maxn 2010
#define maxm 15010
const int inf=1e9;
struct node
{
int y,f,ot,next,c;
}a[maxm*4];int len,first[maxn];
bool vis[maxn];int d[maxn];
int S,T,flow[maxn],pre[maxn],l[maxn];
int mymin(int x,int y){return (x<y)?x:y;}
void ins(int x,int y,int c,int f)
{
len++;int n1=len;a[len].y=y;a[len].c=c;
a[len].f=f;a[len].next=first[x];first[x]=len;
len++;int n2=len;a[len].y=x;a[len].c=-c;
a[len].f=0;a[len].next=first[y];first[y]=len;
a[n1].ot=n2;a[n2].ot=n1;
}
queue<int> q;
int spfa()
{
for (int i=1;i<=T;i++) d[i]=inf,vis[i]=false,pre[i]=-1;
q.push(S);d[S]=0;flow[S]=inf;vis[S]=true;
while (!q.empty())
{
int x=q.front();q.pop();
for (int k=first[x];k!=-1;k=a[k].next)
{
int y=a[k].y;
if (a[k].f<=0) continue;
if (d[y]>d[x]+a[k].c)
{
d[y]=d[x]+a[k].c;
flow[y]=mymin(flow[x],a[k].f);
pre[y]=x;l[y]=k;
if (!vis[y])
{
vis[y]=true;
q.push(y);
}
}
}vis[x]=false;
}
if (pre[T]==-1) return 0;
return flow[T];
}
int MCMF()
{
int delta,ret=0;
while (delta=spfa())
{
int x=T;ret+=(int)delta*d[T];
while (x!=S)
{
a[l[x]].f-=delta;
a[a[l[x]].ot].f+=delta;
x=pre[x];
}
}
return ret;
}
int main()
{
int n,m,i,u,v,w,ans;
scanf("%d%d",&n,&m);
len=0;memset(first,-1,sizeof(first));
S=n*2+1;T=S+1;
for (i=1;i<=n;i++)
{
scanf("%d",&w);
ins(S,i*2,w,1);
ins(S,i*2-1,0,1);
ins(i*2,T,0,1);
}
for (i=1;i<=m;i++)
{
scanf("%d%d%d",&u,&v,&w);
if (u>v) swap(u,v);
ins(u*2-1,v*2,w,1);
}
printf("%d\n",MCMF());
return 0;
}
还有一种方式会快很多。
先假设全都爆发。那么就要
于是构图就变成了:
- S→i ,流量为1,费用为0
- i′→T ,流量为1,费用为0
-
i→j′,i<j
,流量为1,费用为 高速航行所需要的时间-
Aj
可以说是在找那些点走高速会快点 以缩小当前花费吧。如果到 T 所花的费用已经要大于0了,就直接break 就好了。
这么做快了一倍啊。
/**************************************************************
Problem: 1927
User: 564415605
Language: C++
Result: Accepted
Time:1992 ms
Memory:2124 kb
****************************************************************/
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<queue>
#include<iostream>
#include<algorithm>
using namespace std;
#define maxn 2010
#define maxm 40010
const int inf=1e9;
struct node
{
int y,f,ot,next,c;
}a[maxm];int len,first[maxn];
bool vis[maxn];int d[maxn],p[maxn];
int S,T,flow[maxn],pre[maxn],l[maxn];
int mymin(int x,int y){return (x<y)?x:y;}
void ins(int x,int y,int c,int f)
{
len++;int n1=len;a[len].y=y;a[len].c=c;
a[len].f=f;a[len].next=first[x];first[x]=len;
len++;int n2=len;a[len].y=x;a[len].c=-c;
a[len].f=0;a[len].next=first[y];first[y]=len;
a[n1].ot=n2;a[n2].ot=n1;
}
queue<int> q;
int spfa()
{
for (int i=1;i<=T;i++) d[i]=inf,vis[i]=false,pre[i]=-1;
q.push(S);d[S]=0;flow[S]=inf;vis[S]=true;
while (!q.empty())
{
int x=q.front();q.pop();
for (int k=first[x];k!=-1;k=a[k].next)
{
int y=a[k].y;
if (a[k].f<=0) continue;
if (d[y]>d[x]+a[k].c)
{
d[y]=d[x]+a[k].c;
flow[y]=mymin(flow[x],a[k].f);
pre[y]=x;l[y]=k;
if (!vis[y])
{
vis[y]=true;
q.push(y);
}
}
}vis[x]=false;
}
if (pre[T]==-1) return 0;
return flow[T];
}
int MCMF()
{
int delta,ret=0;
while (delta=spfa())
{
if (d[T]>0) break;
int x=T;ret+=(int)delta*d[T];
while (x!=S)
{
a[l[x]].f-=delta;
a[a[l[x]].ot].f+=delta;
x=pre[x];
}
}
return ret;
}
int main()
{
int n,m,i,u,v,w,sum=0;
scanf("%d%d",&n,&m);
len=0;memset(first,-1,sizeof(first));
S=n*2+1;T=S+1;
for (i=1;i<=n;i++)
{
scanf("%d",&p[i]);
sum+=p[i];
ins(S,i*2-1,0,1);
ins(i*2,T,0,1);
}
for (i=1;i<=m;i++)
{
scanf("%d%d%d",&u,&v,&w);
if (u>v) swap(u,v);
ins(u*2-1,v*2,w-p[v],1);
}
printf("%d\n",sum+MCMF());
return 0;
}