2021.12.01 朱刘算法
朱刘算法:
https://blog.csdn.net/u011815404/article/details/85858501
tarjan搞出来的奇奇怪怪的算法:
https://www.luogu.com.cn/blog/CHiCO/solution-p4716
模板题
https://www.luogu.com.cn/problem/P4716
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=210;
const int M=1e4+10;
const int inf=2e9;
int n,m,root,id[N],val[N],vis[N],pre[N];
struct node{
int from,to,val;
}a[M];
inline int read(){
int s=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-')w=-1;
ch=getchar();
}
while(ch<='9'&&ch>='0'){
s=s*10+ch-'0';
ch=getchar();
}
return s*w;
}
inline int zhuLiu(){
int ans=0;
//ans在这里操作有点类似于反悔贪心的意思
while(true){
//找到最小入边
for(int i=1;i<=n;i++)val[i]=inf;
for(int i=1;i<=m;i++){
int u=a[i].from,v=a[i].to;
if(u!=v&&val[v]>a[i].val)val[v]=a[i].val,pre[v]=u;
}
//如果有独立的点就不可能形成树形图
for(int i=1;i<=n;i++)if(i!=root&&val[i]==inf)return -1;
//找环,计算环的个数
int cnt=0;
for(int i=1;i<=n;i++)vis[i]=id[i]=0;
for(int i=1;i<=n;i++){
if(i==root)continue;
ans+=val[i];
//如果i所在的圈之前已经被选过了,现在又选就相当于释放了一条边,变成个不是圈的东西
int v=i;
//找环
while(vis[v]!=i&&!id[v]&&v!=root)vis[v]=i,v=pre[v];
//如果在环上并且没有连到root,染色
if(!id[v]&&v!=root){
id[v]=++cnt;
for(int u=pre[v];u!=v;u=pre[u])id[u]=cnt;
}
}
//没有环,返回
if(cnt==0)break;
//有环,把剩下没有染色的不在环上的节点染色
for(int i=1;i<=n;i++)if(!id[i])id[i]=++cnt;
//更新边的起点、终点、边权
for(int i=1;i<=m;i++){
int u=a[i].from,v=a[i].to;
a[i].from=id[u];a[i].to=id[v];
if(a[i].from!=a[i].to)a[i].val-=val[v];
}
//更新root和这轮朱刘算法过后结点的个数,毕竟缩点了
root=id[root];
n=cnt;
}
return ans;
}
int main(){
n=read();m=read();root=read();
for(int i=1;i<=m;i++){
a[i].from=read();a[i].to=read();a[i].val=read();
}
cout<<zhuLiu();
return 0;
}
练习题:
https://www.luogu.com.cn/problem/P2792
//只要买了就能享受优惠价
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=55;
const int inf=2e9;
int n,m,k,root,num[N],id[N],vis[N],pre[N],true_id[N];
double minn[N],price[N],ans,val[N];
struct node{
int from,to;
double val;
}a[N*N];
inline int read(){
int s=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-')w=-1;
ch=getchar();
}
while(ch<='9'&&ch>='0'){
s=s*10+ch-'0';
ch=getchar();
}
return s*w;
}
inline double zhuLiu(){
while(true){
for(int i=1;i<=n;i++)val[i]=inf;
for(int i=1;i<=m;i++){
int u=a[i].from,v=a[i].to;
if(u!=v&&val[v]>a[i].val)val[v]=a[i].val,pre[v]=u;
}
for(int i=1;i<=n;i++)if(i!=root&&val[i]==inf)return -1;
int cnt=0;
for(int i=1;i<=n;i++)id[i]=vis[i]=0;
for(int i=1;i<=n;i++){
if(i==root)continue;
ans+=val[i];
int v=i;
while(vis[v]!=i&&!id[v]&&v!=root)vis[v]=i,v=pre[v];
if(!id[v]&&v!=root){
id[v]=++cnt;
for(int u=pre[v];u!=v;u=pre[u])id[u]=cnt;
}
}
if(cnt==0)break;
for(int i=1;i<=n;i++)if(!id[i])id[i]=++cnt;
for(int i=1;i<=m;i++){
int u=a[i].from,v=a[i].to;
a[i].from=id[u];a[i].to=id[v];
if(a[i].from!=a[i].to)a[i].val-=val[v];
}
n=cnt;
root=id[root];
}
return ans;
}
int main(){
n=read();
int top=1;
root=1;
for(int i=1;i<=n;i++){
minn[i]=inf;
cin>>price[i];
num[i]=read();
if(!num[i])continue;
true_id[i]=++top;
minn[i]=min(minn[i],price[i]);
a[++m]={1,top,price[i]};
}
k=read();
for(int i=1;i<=k;i++){
int u,v;
double w;
u=read();v=read();cin>>w;
if(!true_id[u]||!true_id[v])continue;
minn[v]=min(minn[v],w);
a[++m]={true_id[u],true_id[v],w};
}
for(int i=1;i<=n;i++)if(num[i]>1)ans+=(num[i]-1)*minn[i];
n=top;
printf("%.2lf",zhuLiu());
return 0;
}