hdu 3395 Special Fish(费用流//KM匹配)

Special Fish

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 1214    Accepted Submission(s): 462


Problem Description
There is a kind of special fish in the East Lake where is closed to campus of Wuhan University. It’s hard to say which gender of those fish are, because every fish believes itself as a male, and it may attack one of some other fish who is believed to be female by it.
A fish will spawn after it has been attacked. Each fish can attack one other fish and can only be attacked once. No matter a fish is attacked or not, it can still try to attack another fish which is believed to be female by it.
There is a value we assigned to each fish and the spawns that two fish spawned also have a value which can be calculated by XOR operator through the value of its parents.
We want to know the maximum possibility of the sum of the spawns.
 


 

Input
The input consists of multiply test cases. The first line of each test case contains an integer n (0 < n <= 100), which is the number of the fish. The next line consists of n integers, indicating the value (0 < value <= 100) of each fish. The next n lines, each line contains n integers, represent a 01 matrix. The i-th fish believes the j-th fish is female if and only if the value in row i and column j if 1.
The last test case is followed by a zero, which means the end of the input.
 


 

Output
Output the value for each test in a single line.
 


 

Sample Input
  
  
3 1 2 3 011 101 110 0
 


 

Sample Output
  
  
6

 

 

题意:有n条鱼,每条鱼都认为自己是公的,如果鱼a认为鱼b是母的,那么a就会攻击b ; 之后就会交配产卵; 每条鱼最多能被攻击一次和攻击别的鱼一次;

每条鱼有一个价值a[i],当 i 攻击 j ,就会得到一个价值为 a[i]^a[j] ;求怎样交配使得总价值最大 ;

1表示认为别人是母的,0表示认为是公的 ;

有两种做法 :

(1)最大权匹配 :    当a认为是b母是的时候,也就是鱼a可以攻击b,当然也可以不攻击,看你怎么分配 ;  a->b连边 ; 权值为a[i]^a[j] ;

      然后就是求一个最大权匹配;注意不一定是完全匹配,因为可能匹配的边很少(认为是母的很少) ;

 

#include<cstdio>
#include<cstring>
#include<map>
#include<vector>
#include<cmath>
#include<cstdlib>
#include<stack>
#include<queue>
#include <iomanip>
#include<iostream>
#include<algorithm>
using namespace std ;
const int N=200 ;
const int inf=1<<30 ;
int w[N][N] ,lx[N],ly[N],fx[N],fy[N],match[N] ;
int n ;
int dfs(int u)
{
	fx[u]=1;
	for(int i = 1 ; i <= n ; i++)
	{
		  if(!fy[i] && lx[u]+ly[i]==w[u][i])
		  { 
		  	    fy[i]=1;
		  	  if(match[i]==-1 || dfs(match[i]))
		  	  {
		  	  	   match[i]=u;
		  	  	   return 1;
		  	  }
		  }
	}
	return 0;
}

int KM()
{
	 memset(ly,0,sizeof(ly));
	 memset(match,-1,sizeof(match));
	 for(int i = 1 ;  i<= n ; i++)
	  {
	  	   lx[i]=-1;
	  	   for(int j = 1 ; j <= n ; j++)
	  	      lx[i] = max(lx[i],w[i][j]);
	  }
	  for(int k = 1 ; k <= n ; k++)
	  {
	  	   while(1)
	  	   {
	  	   	    memset(fx,0,sizeof(fx));
	  	   	    memset(fy,0,sizeof(fy));
	  	   	    if(dfs(k))  break;
	  	   	    int d=inf ;
	  	   	    for(int i = 1 ; i <= n ; i++)
	  	   	        if(fx[i])
	  	   	          for(int j = 1 ; j <= n ; j++)
	  	   	             if(!fy[j])
	  	   	               d=min(d,lx[i]+ly[j]-w[i][j]);
	  	   	for(int i = 1 ; i <= n ; i++)
	  	   	{
	  	   		if(fx[i])  lx[i] -= d ;
	  	   		if(fy[i])  ly[i] += d ;
	  	   	}
	  	   }
	  }
	  int ans=0;
	  for(int i = 1 ; i <= n ; i++)
	     ans += w[match[i]][i];
	return ans ;
}


int main()
{
	   
	   char ch;
	   int a[200];
	   while(~scanf("%d",&n))
	   {   
	      if(n==0)  break ;
	   	   memset(w,0,sizeof(w));
	   	    for(int i = 1 ; i<= n ; i++ )
	   	        scanf("%d",&a[i]);
	   	    for(int i = 1 ; i <= n ; i++)
			   for(int j = 1 ; j <= n ; j++)
			   { 
				     cin>>ch ;
					 if(ch=='1')
					 {
					 	     w[i][j] = a[i]^a[j] ;
					 	 
					 }  
			   }       
	   	   printf("%d\n",KM());
	   	
	   } 
	  return 0;
}



 

最小费用最大流 : 

拆点 i , i+n ;   

因为每条鱼最多 被攻击和主动攻击一次; 所以源点连 每条鱼 i ;  表示每条鱼被攻击一次 ,流量1,费用0;

add(s,i,1,0);

  之后,i+n 连汇点t ,表示主动攻击一次 ; 流量1,费用0 ,add(i+n,t,1,0)

然后,当 a->b ;   add(a,b+n,1, -(a[i]^a[j]) ) ;注意,要求最大价值,所以把权值 边负数就好了 ;

然后没条鱼可以选择不去攻击别的鱼,所以没条鱼 i 连汇点t,相当于绕过 i+n,不走这条边 i+n->t ;通过i+n->t 的都是两条鱼攻击了的 ;

 

 

#include<cstdio>
#include<cstring>
#include<map>
#include<vector>
#include<cmath>
#include<cstdlib>
#include<stack>
#include<queue>
#include <iomanip>
#include<iostream>
#include<algorithm>
using namespace std ;
const int N=200 ;
const int M=N*N ;
const int inf=1<<30 ;
struct node
{
	int u,v,c,cost,next;
}edge[M];
int pp[N],pre[N],head[N],vist[N],dist[N];
int top ;

void add(int u ,int v,int c,int cost)
{
	edge[top].u=u;
	edge[top].v=v;
	edge[top].c=c;
	edge[top].cost=cost;
	edge[top].next=head[u];
	head[u]=top++;
	edge[top].u=v;
	edge[top].v=u;
	edge[top].c=0;
	edge[top].cost=-cost;
	edge[top].next=head[v];
	head[v]=top++;
}

int SPFA(int s,int t)
{
	int u , v ;
	memset(vist,0,sizeof(vist));
	memset(pre,-1,sizeof(pre));
    for(int i = 0 ; i <= t ; i++)  dist[i]=inf ;
    vist[s]=1;dist[s]=0;pre[s]=s;
    queue<int>q;
    q.push(s);
    while(!q.empty())
    {
    	 u=q.front();
    	 q.pop();
    	 vist[u]=0;
    	 for(int i =head[u];i!=-1;i=edge[i].next)
    	 {
    	 	   v=edge[i].v;
    	 	   if(edge[i].c && dist[v] > dist[u]+edge[i].cost)
    	 	   {
    	 	   	    dist[v] = dist[u]+edge[i].cost ;
    	 	   	     pre[v]=u;
    	 	   	     pp[v]=i;
    	 	   	     if(!vist[v]);
    	 	   	     {
    	 	   	     	   vist[v]=1;
    	 	   	     	   q.push(v);
    	 	   	     }
    	 	   }
        }
    }
	if(dist[t]==inf) return 0;
	return 1 ;
}

int MFMC(int s,int t)
{
	int mincost=0,flow=0,minflow ;
	while(SPFA(s,t))
	{
		  minflow=inf;
		  for(int i=t;i!=s;i=pre[i])
		      minflow=min(minflow,edge[pp[i]].c);
		  for(int i=t;i!=s;i=pre[i])
		  {
		  	    edge[pp[i]].c -= minflow;
		  	    edge[pp[i]^1].c += minflow;
		  }    
		  flow += minflow;
		  mincost += dist[t]*minflow ;
	}
	return -mincost ;
}

int main()
{
	int n; 
	int a[200];
	char ch ; 
	while(~scanf("%d",&n))
	{
		   if(n==0)  break;
		   top=0;
		   memset(head,-1,sizeof(head));
		   int s=0,t=2*n+1 ;
		   for(int i = 1 ; i <= n; i++)
		      scanf("%d",&a[i]);
		   for(int i = 1 ; i <= n ; i++)
		    {
		         add(s,i,1,0);
		         add(i,t,1,0);
			     add(i+n,t,1,0);
			 for(int j = 1 ; j <= n ; j++)
		      {
		          cin>>ch ;
				  if(ch=='1')
				  {
				  	  add(i,j+n,1,-(a[i]^a[j])) ;
				  }	
		      }
		   }
		 int ans=MFMC(s,t);
		 printf("%d\n",ans);     
	}
	return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值