这个题就是要选一些边,覆盖所有点,并且边的权值和最小
然后这是一开始建的图:
有如下问题:
1、边数太多
2、最大流不满足问题性质
为了解决2,我们必须让每一个方案都满足最大流的条件
原图的问题是:想用一条流量干和许多流量等价的事(一条流量流1、 2 一条流量流3),那必然会导致最大流和方案是不协调的
所以我们必须让一条流量 干有且仅有一个事
这样最大流就和方案等价了
题目有一个很强的条件,即每个点的都只有一次,
所以我们需要为每一种决策给予一个流量,让费用流选出最优的一条流向t
还有一个很强的用于优化的性质:无论什么点,跳到一个点的费用是相同的
所以就直接从s连向每个点表示就好了
根据dag可知匹配不会有环
这个题的主要思想是既然每个点都会被走到,那就每个点都进行走其他点的决策,而每个点只接受一个决策
这样做相当于离散了一下,跑出来不是具体的方案,是一些关系,这些关系都满足整体条件,利用有且仅有一次的特点推广到每个点的决策,再推广到每个点的选取,
相当于拆点,一个表示到过,一个表示决策,到过不能向决策连边,因为到过要满足最大流的性质
注意:空间,行的字数超过界面了注意滚动条往右查看变量有没有问题
码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
queue<int>q;
bool vis[2000];
int s,t,xia[2000],hou[2000000],qj[2000],tot=-1,ans,v[2000000],cc[2000000],dis[2000],a[2000],j,m,i,n,x,y,z,zhong[2000000];
void jian(int a,int b,int c,int d)
{
++tot;hou[tot]=xia[a];xia[a]=tot;zhong[tot]=b; v[tot]=c;cc[tot]=d;
}
void jia(int a,int b,int c)
{
jian(a,b,1,c);
jian(b,a,0,-c);
}
bool spfa()
{
int i;
memset(vis,0,sizeof(vis));
memset(dis,0x7f,sizeof(dis));
q.push(s);
qj[s]=0;
dis[s]=0;
while(!q.empty())
{
int st=q.front();
q.pop();
vis[st]=0;
for(i=xia[st];i!=-1;i=hou[i])
{
int nd=zhong[i];
if(v[i]>0&&dis[nd]>dis[st]+cc[i])
{
dis[nd]=dis[st]+cc[i];//cout<<st<<" "<<nd<<" "<<dis[nd]<<endl;
qj[nd]=i^1;
if(vis[nd]==0)
{
q.push(nd);
vis[nd]=1;
}
}
}
}
if(dis[t]>1000000000)return 0;
for(i=qj[t];i;i=qj[zhong[i]])
{
v[i]+=1;
v[i^1]-=1;
ans+=cc[i^1];
}
return 1;
}
int main()
{
memset(xia,-1,sizeof(xia));
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++)
{
scanf("%d",&a[i]); //cout<<i<<" "<<a[i]<<endl;
}
for(i=1;i<=m;i++)
{
scanf("%d%d%d",&x,&y,&z);
if(x>y)swap(x,y);
jia(x+n,y,z);
}
s=n*2+2;
t=n*2+3;
for(i=1;i<=n;i++)
{
jia(s,i,a[i]);
jia(s,i+n,0);
jia(i,t,0);
}
while(spfa());
printf("%d",ans);
}