hdu 4009 小树形图
这道题用到图论的一些思维方法 那就是增加超级源点 超级源点到其他所有点的权值是其他点打井的花费 这样的话 就很好表示哪一户打井 如果打井就选择相应边进行了
做题时的错误:
1;以为打井的深度是任意深度的 其实是固定的c 原文的误解让这道题增加了难度
2;写代码时总是忘记变量的匹配了 函数返回值的匹配 还有就是n值忘记改 导致run time error
总结:这道题用的超级源点转换 这道题的cost 也就是边权有两个,一个就是曼哈顿距离,另一个是打井,这两个边权在选择时是可以分立,也就是说两个边权不同时选择;而最小树形图run的只有一个边权,所以这个时候引入源点,将打井的和曼哈顿的化成一个,就是所需的花费,引入源点,那就是打井的花费,选择这条边的话就打井,
#include<stdio.h>
#include<iostream>
#include<queue>
#include<stack>//有向图的最小生成树 俗称最小树形图
#include<vector>// 复杂度 O(NM)
#include<string.h>//点从0开始
#include<math.h>//hdu 4009这道题涉及到图的转换问题
#include<algorithm>//很多图论都涉及到这种问题
#define inf 0x3f3f3f3f//1;分点 也就是将点分成两个或者多个相连
#define type int//2:增加超级源点 使得不改变问题性质 求得答案 这道题运用的就是这个
const int MAXN=1005;//超级原点 到其他点的权值是挖井的费用 也就是相当于每户人家都挖井 也可以求得答案
const int MAXM=40010;
using namespace std;
struct Edge{
int u,v,w;
}edge[MAXN*MAXN];
struct node{
int a,b,c;
}point[MAXN];
int pre[MAXN],id[MAXN],vis[MAXN];
type in[MAXN];
type zhuliu(int root, int V, int E)
{
type ret = 0;
while(true)
{
//1.找最小入边
for(int i = 0; i < V; i++) in[i] = inf;
for(int i = 0; i < E; i++)
{
int u = edge[i].u;
int v = edge[i].v;
if(edge[i].w < in[v] && u != v) {pre[v] = u; in[v] = edge[i].w;}
}
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 = pre[v];
}
if(v != root && id[v] == -1)//缩点
{
for(int u = pre[v]; u != v; u = pre[u]) id[u] = cnt;
id[v] = cnt++;
}
}
if(cnt == 0) 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;
}
type Dist(node a,node b)
{
return abs(a.a-b.a)+abs(a.b-b.b)+abs(a.c-b.c);
}
int main()
{
int n,m,x,y,z,v,k;
while(scanf("%d%d%d%d",&n,&x,&y,&z) && n)
{
int l=0;
for(int i=1;i<=n;i++)
scanf("%d%d%d",&point[i].a,&point[i].b,&point[i].c);
for(int i=1;i<=n;i++)
{
scanf("%d",&k);
for(int j=0;j<k;j++)
{
scanf("%d",&v);
if(i!=v)//消除自环
{
edge[l].u=i;edge[l].v=v;
edge[l].w=Dist(point[i],point[v])*y;
if(point[i].c<point[v].c) edge[l].w+=z;//如果低于
l++;
}
}
}
for(int i=1;i<=n;i++)//源点到其他点
{
edge[l].u=0;edge[l].v=i;
edge[l++].w=point[i].c*x;
}
int ans=zhuliu(0,n+1,l);
if(ans==-1)printf("poor XiaoA\n");
else printf("%d\n",ans);
}
return 0;
}