HDU 3435 KM算法或者最小费用最大流

来源:互联网 发布:程序员推荐书单 编辑:程序博客网 时间:2024/05/16 10:04

A new Graph Game

Time Limit: 8000/4000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 1010    Accepted Submission(s): 454


Problem Description
An undirected graph is a graph in which the nodes are connected by undirected arcs. An undirected arc is an edge that has no arrow. Both ends of an undirected arc are equivalent--there is no head or tail. Therefore, we represent an edge in an undirected graph as a set rather than an ordered pair.
Now given an undirected graph, you could delete any number of edges as you wish. Then you will get one or more connected sub graph from the original one (Any of them should have more than one vertex).
You goal is to make all the connected sub graphs exist the Hamiltonian circuit after the delete operation. What’s more, you want to know the minimum sum of all the weight of the edges on the “Hamiltonian circuit” of all the connected sub graphs (Only one “Hamiltonian circuit” will be calculated in one connected sub graph! That is to say if there exist more than one “Hamiltonian circuit” in one connected sub graph, you could only choose the one in which the sum of weight of these edges is minimum).
  For example, we may get two possible sums:

(1)  7 + 10 + 5 = 22
(2)  7 + 10 + 2 = 19
(There are two “Hamiltonian circuit” in this graph!)
 

Input
In the first line there is an integer T, indicates the number of test cases. (T <= 20)
In each case, the first line contains two integers n and m, indicates the number of vertices and the number of edges. (1 <= n <=1000, 0 <= m <= 10000)
Then m lines, each line contains three integers a,b,c ,indicates that there is one edge between a and b, and the weight of it is c . (1 <= a,b <= n, a is not equal to b in any way, 1 <= c <= 10000)
 

Output
Output “Case %d: “first where d is the case number counted from one. Then output “NO” if there is no way to get some connected sub graphs that any of them exists the Hamiltonian circuit after the delete operation. Otherwise, output the minimum sum of weight you may get if you delete the edges in the optimal strategy.

 

Sample Input
33 41 2 52 1 22 3 103 1 7 3 21 2 31 2 42 21 2 31 2 4
 

Sample Output
Case 1: 19Case 2: NOCase 3: 6
Hint
In Case 1:You could delete edge between 1 and 2 whose weight is 5. In Case 2:It’s impossible to get some connected sub graphs that any of them exists the Hamiltonian circuit after the delete operation.
 

Author
AekdyCoin
 

Source

2010 ACM-ICPC Multi-University Training Contest(1)——Host by FZU


分析:

像这样构成圈并且每个点只能属于一个圈的题, 可以转化成2 分图, 每个点只能属于一个圈, 那么出度和入度必定为1 , 那么把一个点拆开i, i`, i控制入读, i` 控制出度, 流量只能为1 。 那么对于原来图中有的边 可以 i - > j`, j - > i`;连起来构图, 然后建立超级远点s,超级汇点t,s - > i , i` - > t ; 然后求最小费用流。。这样就保证了每个点只能属于一个圈, 因为入读 == 出度 == 1 ;这类也问题可以 做为判断性问题出。

    关键是拆点建图,把每个顶点拆成i和i+n。附加一个源点S和汇点T。

    S与1~n建边,容量为1,花费为0;

    n+1~n*2与T建边,容量为1,花费为0。

    若ab右边,a与b+n建边,容量为1,花费为c,b与a+n建边,容量为1,花费为c。

    求最小费用流。

    最后判断时,因为每个点都存在某个双连通分量中,那么每个点(1~n)的总流量为0。若某个点的总流量不为0,输出NO。

    否则,输出最小费用流。

不过这题显然可以用KM算法当作求二分图的最大权值完美匹配.此时就是一道Km算法的模板题了:

#include <cstdio>//KM算法写法#include <cstring>#include <queue>#include <algorithm>using namespace std;const int INF = 999999999;const int N = 1005;int graph[N][N];int lx[N], ly[N];bool visitx[N], visity[N];int slack[N];int match[N];int n,m;bool Hungary(int u){    int temp;    visitx[u] = true;    for(int i = 1; i <= n; ++i)    {        if(visity[i])            continue;        else        {            temp = lx[u] + ly[i] - graph[u][i];            if(temp == 0) //相等子图            {                visity[i] = true;                if(match[i] == -1 || Hungary(match[i]))                {                    match[i] = u;                    return true;                }            }            else //松弛操作                slack[i] = min(slack[i], temp);        }    }    return false;}void KM(){    int temp;    memset(match,-1,sizeof(match));    memset(ly,0,sizeof(ly));    for(int i = 1;i <= n;i++) //定标初始化        lx[i] = -INF;    for(int i =1;i<=n;i++)        for(int j=1;j<= n;j++)            lx[i] = max(lx[i], graph[i][j]);    for(int i = 1; i <= n;i++)    {        for(int j = 1; j <= n;j++)            slack[j] = INF;        while(1)        {            memset(visitx,false,sizeof(visitx));            memset(visity,false,sizeof(visity));            if(Hungary(i))                break;            else            {                temp = INF;                for(int j = 1; j <= n; ++j)                    if(!visity[j]) temp = min(temp, slack[j]);                for(int j = 1; j <= n; ++j)                {                    if(visitx[j]) lx[j] -= temp;                    if(visity[j]) ly[j] += temp;                    else slack[j] -= temp;                }            }        }    }}int main(){    int tcase;    int t= 1;    scanf("%d",&tcase);    while(tcase--){        scanf("%d%d",&n,&m);        for(int i=1;i<=n;i++){            for(int j=1;j<=n;j++){                graph[i][j] = -INF;            }        }        for(int i=1;i<=m;i++){            int u,v,w;            scanf("%d%d%d",&u,&v,&w);            if(u==v) continue;            graph[u][v] = graph[v][u] = max(graph[u][v],-w);        }        KM();        int ans = 0;        bool flag = false;        for(int i=1;i<=n;i++){            if(match[i]==-1||graph[match[i]][i]==-INF){                flag = true;                break;            }            ans+=graph[match[i]][i];        }        printf("Case %d: ",t++);        if(flag)printf("NO\n");        else printf("%d\n",-ans);    }    return 0;}
然后是最小费用最大流的写法,不过应该卡时间,也有可能是我写搓了=-代码仅供参考,请勿直接复制!!

#include<cstdio>//最小费用最大流写法#include<cstring>#include<iostream>#include<algorithm>#include<queue>using namespace std;const int maxn=55555;const int inf =0x3f3f3f3f;struct Node{    int u,v,w,c,next;}edge[maxn];int pre[maxn],vis[maxn],head[maxn],dis[maxn];int cnt=0;int n,m,s,t;void addedge(int u,int v,int w,int c){    edge[cnt].u=u;    edge[cnt].v=v;    edge[cnt].w=w;    edge[cnt].c=c;    edge[cnt].next=head[u];    head[u]=cnt++;    edge[cnt].u=v;    edge[cnt].v=u;    edge[cnt].w=0;    edge[cnt].c=-c;    edge[cnt].next=head[v];    head[v]=cnt++;}bool SPFA(){    memset(dis,0x3f,sizeof(dis));    memset(pre,-1,sizeof(pre));    memset(vis,0,sizeof(vis));    vis[s]=1;    dis[s]=0;    queue<int>q;    q.push(s);    while(!q.empty()){        int u=q.front();        q.pop();        vis[u]=false;        for(int j=head[u]; j!=-1; j=edge[j].next)        {            int v=edge[j].v;            if(edge[j].w&&dis[u]+edge[j].c<dis[v])            {                dis[v]=dis[u]+edge[j].c;                pre[v]=j;                if(!vis[v])                {                    vis[v]=true;                    q.push(v);                }            }        }    }    return dis[t]!=inf;}int Maxcostflow(){    int res=0,u;    while(SPFA()){        u=pre[t];        res+=dis[t];        while(u!=-1)        {            edge[u].w--;            edge[u^1].w++;            u=pre[edge[u].u];        }    }    return res;}int main(){    int test;    scanf("%d",&test);    for(int item = 1; item <= test; item++)    {        cnt = 0;        memset(head,-1,sizeof(head));        scanf("%d%d",&n,&m);        s=0;        t=2*n+1;        int a,b,c;        for(int i=1; i<=m; i++)        {            scanf("%d%d%d",&a,&b,&c);            addedge(a,b+n,1,c);            addedge(b,a+n,1,c);        }        for(int i=1; i<=n; i++)        {            addedge(s,i,1,0);            addedge(i+n,t,1,0);        }        int ans=Maxcostflow();        int j;        for(j=head[s]; j!=-1; j=edge[j].next)            if(edge[j].w!=0)                break;        if(j==-1) printf("Case %d: %d\n",item,ans);        else printf("Case %d: NO\n",item);    }    return 0;}
最美的风景给最有能力的人欣赏~


0 0
原创粉丝点击