一天到晚没事干天天想着炸机房。。。。
我:这道题朱刘算法裸题。。
cja:什么,猪牛算法,什么鬼。。。莫非是诸葛亮和刘备发明的
我:。。。。。。。
#include<cstdio>
#include<cstring>
#include<iostream>
#define maxn 10021
using namespace std;
int n,m,times[maxn],nu[maxn],tot,head[maxn],pre[maxn],vis[maxn],id[maxn];
double cost[maxn],in[maxn],Min[maxn];
struct edge{int a,b;double w;}e[maxn];
void adde(int a,int b,double c){
e[++tot].a=a,e[tot].b=b,e[tot].w=c;
}
double solve(){
double ans=0;int rt=n;
while(true){
for(int i=1;i<=n;i++)in[i]=1e9;//找对于每一个点来说最小的入边
for(int a,b,i=1;i<=tot;i++){
a=e[i].a,b=e[i].b;
if(a!=b&&e[i].w<in[b])in[b]=e[i].w,pre[b]=a; //更新每一个点的最小的入边以及这条入边是从哪一个点过来的
}in[rt]=0; //根的入边没有
int cnt=0;
for(int i=1;i<=n;i++)id[i]=vis[i]=0; //清空标号和懒惰标记
for(int i=1;i<=n;i++){
ans+=in[i]; //答案先加上这每一个点的入边,但是这样以后就会产生很多的环
int j;
//找到i这个点所在的环,如果找到了根节点的话就直接停止,因为已经没有环了
//如果找到的节点的编号也是i的话说明这个环已经找完了
//如果下一个节点已经在一个环内了,说明当前节点之前的是一条链然后上面连着
for(j=i; vis[j]!=i && !id[j] && j!=rt;j=pre[j])vis[j]=i;
//如果j是已经有标号了或者说j就是根的话就说明i之前的并不是对环
if(!id[j]&&j!=rt){
id[j]=++cnt;
//然后再来一个循环
for(int k=pre[j];k!=j;k=pre[k])
id[k]=cnt;
}
}
//说明已经没有环了
if(cnt==0)return ans;
//每一单独的点且不在环中就当做一个单独的单点的环处理
//也就是说在这里重新标号,对于一个环的环就当做一个新的节点然后删除最大的一条边
for(int i=1;i<=n;i++)if(!id[i])id[i]=++cnt;
//枚举每一条边
for(int i=1;i<=tot;i++){
double v=in[e[i].b];//边权
//如果这个点在后面还会被选上那么就表明之前的边并不会被选上
//所以新的边权要剪掉之前选上的边权
e[i].a=id[e[i].a],e[i].b=id[e[i].b];
if(e[i].a!=e[i].b)e[i].w-=v;
}
n=cnt,rt=id[rt];
}
}
int main(){
scanf("%d",&n);int N=n;
for(int i=1;i<=n;i++){
scanf("%lf%d",cost+i,times+i);
if(times[i])adde(n+1,tot+1,cost[i]),Min[i]=cost[i],nu[i]=tot;
}n=tot+1;
for(int i=1;i<=tot;i++)e[i].a=n;
scanf("%d",&m);int a,b;double c;
for(int i=1;i<=m;i++){
scanf("%d%d%lf",&a,&b,&c);
if(times[a]&×[b]){
Min[b]=min(Min[b],c);
adde(nu[a],nu[b],c);
}
}
double ans=0;
ans+=solve();
for(int i=1;i<=N;i++){
if(times[i])ans+=(times[i]-1)*Min[i];
}
printf("%.2lf",ans);
return 0;
}