zoukankan      html  css  js  c++  java
  • E.Emotion(2019武大校赛现场赛)(吉比特杯第二届湖北省程序设计大赛)(二分图匹配) 解题报告 Apare_xzc

    E.Emotion(2019武大校赛现场赛)(吉比特杯第二届湖北省程序设计大赛)(二分图匹配) 解题报告

    xzc 2019/4/18

    CCNU_你们好强啊我们都是面包手


    题意:
      GHK这个人喜欢玩“问道”这款手游,他喜欢收集一款飞行灵兽。飞行灵兽可以升级,升级以后它的速度,伤害,防御和其它属性都会提升。他发现幽灵之魂(ghosts)和帝王之魂(impreial spirits)可以结合,从而使得飞行灵兽的属性提升。
      现在他手中的幽灵之魂和帝王之魂一共有N个,现在GHK的死对头TYT要求GHK给它尽可能多的ghosts和imperia spirits。GHK当然不想让死对头得到好处,他决定挑出尽可能多的K个两种魂,且这K个任意两个不能配对。他想知道K最大可以为多少。
      注:两种魂共N个,序号为0~N-1,x和y代表一对可结合的两种魂的序号(若x是ghost,那么y一定是imperial spirit,不然的话就x是imperial spirit,y是ghost)。
      x和y满足下列关系:

    当x>=N/2时 y = ((A*x)^B)%(N/2)  
    当x<N/2时  y = ((B*x)^A)%(N/2)+(N/2)
    N/2为整数除法,%为取模运算,^为按位异或
    A,B是题目给出的参数
    

    输入:
    第一行一个T,表示有T组测试数据
    接下来T行,每行三个整数N,A,B


    输出:
    T行,每行一个整数表示答案


    数据范围:
    1<=T<=10
    2<=N<=3000
    0<=A,B<=1E6


    Example input:

    2
    6 0 0
    6 3 4
    

    Example output:

    4
    3
    

    分析(上题解):

    • 容易发现 0~N/2-1 喜欢的数字是 N/2~N , N/2~N 喜欢的数字是 0~N/2-1
    • 所以,0~N-1构成一张二分图,抽取k个点,互相没有感情,就是求最大独立集
    • 最大独立集合 = 总点数 - 最大匹配数
    • 求二分图最大匹配可以网络流或者匈牙利算法

      我用的是网络流算法求最大匹配数(网络流大法好)
    思路是这样的:

    1. 设要匹配的二分图的两个点集合为U和V,设n=floor(N/2)则U中点的编号都在[0,n-1]的范围内,V中点的编号都在[n,N-1]的范围内
    2. 我们建立原点S和汇点T,不妨给S编号为N号节点,给T编号为N+1号节点
    3. 我们开始构图。原点S对于集合U中的每一个元素连一条容量为1的有向边。集合V的每一个每一个元素连一条道汇点T的容量为1的有向边。然后原来U,V之间的边照样连,容量也为1。
    4. 构图之后,由于网络流的性质,从原点到汇点的的最大流就是最大匹配数量

    我目前只会Ford-Fulkerson,看了网上很多板子,用数组模拟链表改写了


    我的代码:

    /*
    E. Emotion 
    Author: CCNU xzc
    time: 560ms
    state: AC
    2019/4/17 17:11
    */
    #include <bits/stdc++.h>
    #define For(i,a,b) for(register int i=(a);i<=(b);++i)
    #define Rep(i,a,b) for(register int i=(a);i>=(b);--i)
    #define Mst(a,b) memset(a,(b),sizeof(a))
    using namespace std;
    const int maxn = 310;
    const int maxm = 2e3;
    int A,B,N,n; //N<=300 
    struct Edge{
    	int from,to,Next,rev; 
    }edge[maxm];
    int head[maxn],tot;
    int flow[maxm],pre[maxn],IdEdge[maxn],st,ed;
    int f2(int);
    int f1(int);
    void initG();
    void addedge(int,int,int); 
    int bfs();
    void solve(); 
    int main()
    {
        //freopen("in.txt","r",stdin);
        //freopen("out.txt","w",stdout);
        int T;scanf("%d",&T);
        while(T--)
        {
        	scanf("%d%d%d",&N,&A,&B);
        	n = N/2;
        	initG();
        	st = N;ed = N+1;
        	For(u,0,n-1)
        	{
        		int v = f1(u);
        		addedge(st,u,1);
        		addedge(u,v,1);
    		}
    		For(u,n,N-1)
    		{
    			int v = f2(u);
    			addedge(u,ed,1);
    			addedge(v,u,1);
    		} 
    		solve();
    	}
        return 0;
    }
    int f2(int x)
    {
    	return ((A*x)^B)%n;
    }
    int f1(int x)
    {
    	return ((B*x)^A)%n+n;
    }
    void initG()
    {
    	tot = 0;
    	Mst(head,-1);
    	Mst(flow,0);//流量置为0 
    }
    void addedge(int u,int v,int f)
    {
    	flow[tot] += f;
    	edge[tot].from = u;
    	edge[tot].to = v;
    	edge[tot].rev = tot+1;
    	edge[tot].Next = head[u];
    	head[u] = tot++;//下面开始建反向边 
    	edge[tot].from = v;
    	edge[tot].to = u;
    	edge[tot].rev = tot-1;
    	edge[tot].Next = head[v];
    	head[v] = tot++; 
    }
    int bfs()
    {
    	Mst(pre,-1);
    	queue<int> Q;
    	pre[st] = 0;
    	Q.push(st);
    	bool flag = false;
    	while(!Q.empty()&&!flag)
    	{
    		int u = Q.front();Q.pop();
    		for(int i=head[u];i!=-1;i=edge[i].Next)
    		{
    			int v = edge[i].to;
    			if(flow[i]>0&&pre[v]==-1)
    			{
    				pre[v] = u;
    				IdEdge[v] = i;
    				Q.push(v);
    				if(v==ed) 
    				{
    					flag = true;
    					break;	
    				}	
    			} 
    		} 
    	}
    	if(!flag) return 0;
    	int v = ed;
    	int add = 2e9;
    	while(v!=st)
    	{
    		int id = IdEdge[v];
    		add = min(add,flow[id]);
    		v = pre[v];
    	}
    	v = ed;
    	while(v!=st)
    	{
    		int id = IdEdge[v];
    		int r = edge[id].rev;
    		flow[id] -= add;
    		flow[r]  += add;
    		v = pre[v];
    	}
    	return add;
    }
    void solve()
    {
    	int ans = 0, add = 0;
    	while(1)
    	{
    		add = bfs(); 
    		if(!add) break;
    		ans += add;
    	} 
    	printf("%d
    ",N-ans);
    }
    
    

    附:所有测试数据:

    input:

    12
    6 0 0
    6 3 4
    211 418178 32278
    230 282923 473099
    255 218252 352410
    74 998575 854744
    57 427806 733718
    33 491362 20360
    50 0 0
    300 12345 56789
    150 0 3
    200 1 0
    

    Answer:

    4
    3
    126
    129
    145
    43
    38
    24
    48
    177
    124
    100
    

    附:武大出题人的标程(匈牙利算法)

    #include <algorithm>
    #include <cstdio>
    #include <iostream>
    #include <cstring>
    using namespace std;
    #define maxn 305
    int cx[maxn],cy[maxn];
    int visited[maxn];
    int edges[maxn][maxn];
    int N,A,B;
    
    int path(int u){
    	int v;
    	for(v=N/2;v<N;v++){
    		if(edges[u][v] && !visited[v]){
    			visited[v] = 1;
    			if(cy[v]==-1 || path(cy[v])){
    				cx[u] = v;
    				cy[v] = u;
    				return 1;
    			}
    		}
    	}
    	return 0;
    }
    int maxMatch(){
    	int res = 0;
    	memset(cx,0xff,sizeof(cx));
    	memset(cy,0xff,sizeof(cy));
    	int Nx = N/2;
    	for(int i=0;i<Nx;i++){
    		if(cx[i]==-1){
    			memset(visited,0,sizeof(visited));
    			res += path(i);
    		}
    	}
    	return res;
    }
    
    void build(){
    	memset(edges,0,sizeof(edges));
    	for(int i=0;i<N;i++){
    		int y,x=i;
    		if(i>=N/2){
    			y = ((A*x)^B)%(N/2);
    			edges[y][i] = 1;
    		}else{
    			y = ((B*x)^A)%(N/2)+N/2;
    			edges[i][y] = 1;
    		}
    	}
    }
    int main(){
    	 freopen("in.txt","r",stdin);
    	 freopen("stdout.txt","w",stdout);
    
    	int T;
    	cin>>T;
    	while(T--){
    		cin>>N>>A>>B;
    		build();
    		int ans = N - maxMatch();
    		cout<<ans<<endl;
    	}
    	return 0;
    }
    

  • 相关阅读:
    jpype
    Java获取类中的所有方法
    SQL中INNER JOIN、LEFT JOIN、RIGHT JOIN、FULL JOIN区别
    如何用命令将本地项目上传到git
    Java连接Mysql:通过配置文件
    lsof -i:port_number
    yum install lsof
    Git的基本使用方法(受益匪浅)
    [后端]gitlab之gitlab-ci自动部署
    centos7安装redis-4.0.1集群
  • 原文地址:https://www.cnblogs.com/Apare-xzc/p/12243648.html
Copyright © 2011-2022 走看看