HDOJ 3251 - Being a Hero 构图最小割

                 题意:

                         国王决定给你一些城市让你选择...首都是1号点..国王要求不能从1号点出发走到选择的城市..可以花一定代价去掉路...选择每个城市可以获得一定的分数..问可以获得的最大分数..以及要去掉的边的序号...

                 题解: 

                         对于所有的边..起点往终点做边..容量为其去掉的代价...所有能选的城市与超级汇点做边..容量为选其能获得的分数..将所有可能获得的分数加起来减去构图的最大流(最小割)...就是答案..

                         题目还要求输出去掉了哪些边..那么从起点开始标号..如果容量为0就不能沿着这条边往下走..然后扫描所有的边..若其起点被标记了.终点未被标记..则该边是最小割中的割边....

                 总结:

                         这是一类比较典型的最小割模型...特点是要以什么代价选择什么可以获得一些分数..那么将可以获得的分数加起来..然后把代价和获得的分数都做边...假设跑出的最小割有获得分数的..那么总分数在减的时候相当于不选它...相反..若最小割没有某个获得的分数..相减后就代表要选这个分数的点....如果去掉的时代价..那么做减法相当于要付出这些代价...


Program:

#include<iostream>    
#include<algorithm>    
#include<stdio.h>    
#include<string.h>  
#include<time.h> 
#include<map> 
#include<math.h>    
#include<queue>    
#define MAXN 1505 
#define MAXM 600005
#define oo 1000000007    
#define ll long long    
using namespace std;   
struct Dinic            
{            
       struct node          
       {           
             int c,u,v,next;          
       }edge[MAXM];          
       int ne,Tn,W[MAXM][2],head[MAXN];         
       int cur[MAXN], ps[MAXN], dep[MAXN];
       bool mark[MAXN];          
       void initial()          
       {          
             ne=2,Tn=0;         
             memset(head,0,sizeof(head));  
       }          
       void addedge(int u, int v,int c)          
       {           
             edge[ne].u=u,edge[ne].v=v,edge[ne].c=c,edge[ne].next=head[u];   
             head[u]=ne++;      
             edge[ne].u=v,edge[ne].v=u,edge[ne].c=0,edge[ne].next=head[v];          
             head[v]=ne++;          
       }        
       void addT(int u,int v)
       {
             W[++Tn][0]=u,W[Tn][1]=v;
       }  
       void dfs(int x)
       {
             mark[x]=true;
             for (int k=head[x];k;k=edge[k].next)
                 if (!mark[edge[k].v] && edge[k].c) 
                     dfs(edge[k].v);
       }
       int MaxFlow(int s,int t,int &num,int *ans)          
       {                               
             int tr, res = 0;          
             int i,j,k,f,r,top;          
             while(1)          
             {          
                    memset(dep, -1, sizeof(dep));          
                    for(f=dep[ps[0]=s]=0,r=1;f!= r;)          
                       for(i=ps[f++],j=head[i];j;j=edge[j].next)          
                         if(edge[j].c&&dep[k=edge[j].v]==-1)          
                         {          
                               dep[k]=dep[i]+1;          
                               ps[r++]=k;          
                               if(k == t){  f=r; break;  }          
                         }          
                    if(dep[t]==-1) break;          
                    memcpy(cur,head,sizeof(cur));          
                    i=s,top=0;          
                    while(1)          
                    {          
                         if(i==t)          
                         {          
                               for(tr=oo,k=0;k<top;k++)          
                                  if(edge[ps[k]].c<tr)          
                                     tr=edge[ps[f=k]].c;          
                               for(k=0;k<top;k++)          
                               {          
                                     edge[ps[k]].c-=tr;          
                                     edge[ps[k]^1].c+=tr;          
                               }          
                               i=edge[ps[top=f]].u;          
                               res+= tr;          
                         }          
                         for(j=cur[i];cur[i];j=cur[i]=edge[cur[i]].next)           
                             if(edge[j].c && dep[i]+1==dep[edge[j].v]) break;           
                         if(cur[i])  ps[top++]=cur[i],i=edge[cur[i]].v;           
                         else          
                         {          
                                 if(!top) break;          
                                 dep[i]=-1;          
                                 i=edge[ps[--top]].u;          
                         }          
                   }          
             }      
             memset(mark,false,sizeof(mark)); 
             dfs(s);
             num=0;   
             for (i=1;i<=Tn;i++)
                if (mark[W[i][0]] && !mark[W[i][1]])
                   ans[++num]=i;
             return res;          
      }          
}T;      
int ans[MAXM]; 
int main()   
{     
      int C,cases,s,e,n,m,i,x,y,d,f,sum,num; 
      scanf("%d",&C); 
      for (cases=1;cases<=C;cases++)
      {
               scanf("%d%d%d",&n,&m,&f),sum=0;
               s=1,e=n+1,T.initial();
               while (m--)
               {
                       scanf("%d%d%d",&x,&y,&d);
                       T.addedge(x,y,d),T.addT(x,y);
               }            
               while (f--)
               {
                       scanf("%d%d",&x,&d),sum+=d;
                       T.addedge(x,e,d);
               } 
               printf("Case %d: %d\n",cases,sum-T.MaxFlow(s,e,num,ans));
               printf("%d",num);
               for (i=1;i<=num;i++) printf(" %d",ans[i]);
               printf("\n");
      }  
      return 0;  
}  


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值