template
#include<bits/stdc++.h>
const int maxn=1e2+50,maxm=1e4+50,inf=0x3f3f3f3f;
namespace IO
{
char buf[1<<15],*fs,*ft;
inline char getc() { return (ft==fs&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),ft==fs))?0:*fs++; }
template<typename T>inline void read(T &x)
{
x=0;
T f=1, ch=getchar();
while (!isdigit(ch) && ch^'-') ch=getchar();
if (ch=='-') f=-1, ch=getchar();
while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
x*=f;
}
char Out[1<<24],*fe=Out;
inline void flush() { fwrite(Out,1,fe-Out,stdout); fe=Out; }
template<typename T>inline void write(T x,char str)
{
if (!x) *fe++=48;
if (x<0) *fe++='-', x=-x;
T num=0, ch[20];
while (x) ch[++num]=x%10+48, x/=10;
while (num) *fe++=ch[num--];
*fe++=str;
}
}
using IO::read;
using IO::write;
namespace Edmonds
{
int n,m,r;
int pre[maxn];//fa[y] 当前连到 y 点的最短边的 x
int In[maxn];//In[x] 为当前连到 x 点的最短边的边权
int vis[maxn];//vis[x] 代表 x 所在链的代表元素,类似并查集
int id[maxn];//id[x] 代表 x 节点在第 id[x] 个环中
struct Orz{int x,y,z;}e[maxm];
inline int zhu_liu()
{
int ans=0;
while (1)
{
for (int i=1; i<=n; ++i) In[i]=inf;
for (int i=1,x,y,z; i<=m; ++i)
{
x=e[i].x, y=e[i].y, z=e[i].z;
if (x^y && z<In[y]) pre[y]=x, In[y]=z;//遍历所有边,对每个点找到最小的入边
}
for (int i=1; i<=n; ++i)
if (i^r && In[i]==inf) return -1;//判定无解
int cnt=0;//cnt 代表当前图环的数量
for (int i=1; i<=n; ++i) vis[i]=id[i]=0;
for (int i=1; i<=n; ++i)
{
if (i==r) continue;
ans+=In[i];
int x=i;
for ( ; x^r && vis[x]^i && !id[x]; x=pre[x]) vis[x]=i;//找环
if (x^r && !id[x])
{
id[x]=++cnt;//把环上的点标记为同一点
for (int y=pre[x]; y^x; y=pre[y]) id[y]=cnt;
}
}
if (!cnt) break;//无环,得到解
for (int i=1; i<=n; ++i)
if (!id[i]) id[i]=++cnt;
//建立新图,缩点,重新标记
for (int i=1,x,y; i<=m; ++i)
{
x=e[i].x, y=e[i].y;
if ( (e[i].x=id[x])^(e[i].y=id[y]) ) e[i].z-=In[y];//修改边权
}
n=cnt,r=id[r];
}
return ans;
}
}
using namespace Edmonds;
int main()
{
read(n);read(m);read(r);
for (int i=1; i<=m; ++i) read(e[i].x),read(e[i].y),read(e[i].z);
write(zhu_liu(),'\n');
IO::flush();
return 0;
}
HDU 2121 Ice_cream’s world II
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e3+10;
const int inf=0x7f7f7f7f;
struct node//边的权和顶点
{
int u, v;
ll w;
}edge[maxn*maxn];
int head[maxn], id[maxn], vis[maxn], n, m, pos;
ll in[maxn];//存最小入边权,head[v]为该边的起点
inline ll Directed_MST(int root, int V, int E)
{
ll ret = 0;//存最小树形图总权值
while (1)
{
//1.找每个节点的最小入边
for (int i = 0; i < V; ++i)
in[i] = inf;//初始化为无穷大
for (int i = 0; i < E; ++i)//遍历每条边
{
int u = edge[i].u, v = edge[i].v;
if (edge[i].w < in[v] && u != v)//说明顶点v有条权值较小的入边 记录之
{
head[v] = u;//节点u指向v
in[v] = edge[i].w;//最小入边
if (u == root)//这个点就是实际的起点
pos = i;
}
}
for (int i = 0; i < V; ++i)//判断是否存在最小树形图
{
if (i == root) continue;
if (in[i] == inf) return -1;
}//除了根以外有点没有入边,则根无法到达它,说明它是独立的点,一定不能构成树形图
//2.找环
int cnt = 0;//记录环数
memset(id, -1, sizeof(id));
memset(vis, -1, sizeof(vis));
in[root] = 0;
for (int i = 0; i < V; ++i) //标记每个环
{
ret += in[i];//记录权值
int v = i;
while (vis[v] ^ i && id[v] == -1 && v ^ root)
{
vis[v] = i;
v = head[v];
}
if (v ^ root && id[v] == -1)
{
for (int u = head[v]; u ^ v; u = head[u])
id[u] = cnt;//标记节点u为第几个环
id[v] = cnt++;
}
}
if (!cnt)
break; //无环 则break
for (int i = 0; i < V; ++i)
if (id[i] == -1)
id[i] = cnt++;
//3.建立新图 缩点,重新标记
for (int i = 0; i < E; ++i)
{
int u = edge[i].u;
int v = edge[i].v;
edge[i].u = id[u];
edge[i].v = id[v];
if (id[u] ^ id[v])
edge[i].w -= in[v];
}
V = cnt;
root = id[root];
}
return ret;
}
int main()
{
while (scanf("%d%d", &n, &m) != EOF)
{
ll sum = 0;
for (int i = 0; i < m; ++i)
{
scanf("%d%d%lld", &edge[i].u, &edge[i].v, &edge[i].w);
++edge[i].u , ++edge[i].v;
sum += edge[i].w;
}
++sum;
for (int i = m; i < m + n; ++i)
{//增加超级节点0,节点0到其余各个节点的边权相同(此题中 边权要大于原图的总边权值)
edge[i].u = 0;
edge[i].v = i - m + 1;
edge[i].w = sum;
}
ll ans = Directed_MST(0, n + 1, m + n);
//n+1为总结点数,m+n为总边数
//ans代表以超级节点0为根的最小树形图的总权值,
//将ans减去sum,如果差值小于sum,说明节点0的出度只有1,说明原图是连通图
//如果差值>=sum,那么说明节点0的出度不止为1,说明原图不是连通图
if (ans == -1 || ans - sum >= sum)
puts("impossible");
else
printf("%lld %d\n", ans - sum, pos - m);
puts("");
}
return 0;
}
summary
认识自己的无知是认识世界的最可靠的方法。——《随笔集》