zoukankan      html  css  js  c++  java
  • 洛谷 P4249 [WC2007]剪刀石头布

    在一些一对一游戏的比赛(如下棋、乒乓球和羽毛球的单打)中,我们经常会遇到 (A) 胜过 (B)(B) 胜过 (C)(C) 又胜过 (A) 的有趣情况,不妨形象的称之为剪刀石头布情况。有的时候,无聊的人们会津津乐道于统计有多少这样的剪刀石头布情况发生,即有多少对无序三元组 ((A,B,C)),满足其中的一个人在比赛中赢了另一个人,另一个人赢了第三个人而第三个人又胜过了第一个人。注意这里无序的意思是说三元组中元素的顺序并不重要,将 ((A, B, C))((A, C, B))((B, A, C))((B, C, A))((C, A, B))((C, B, A)) 视为相同的情况。

    (N) 个人参加一场这样的游戏的比赛,赛程规定任意两个人之间都要进行一场比赛:这样总共有 (frac{N*(N-1)}{2}) 场比赛。比赛已经进行了一部分,我们想知道在极端情况下,比赛结束后最多会发生多少剪刀石头布情况。即给出已经发生的比赛结果,而你可以任意安排剩下的比赛的结果,以得到尽量多的剪刀石头布情况。

    (N leq 100)


    简单来说就是给你一个竞赛图,有一些边没有定向,你要定向之后使得图中的三元环个数最多。

    那么考虑把问题反过来,最小化非三元环子图个数,用 (nchoose{3}) 来减去。

    于是对三元环定向,只考虑入度,然后可以发现,一个点任意两个入度都会贡献 (1) ,那么一个入度为 (x) 的点的贡献就是 (xchoose2) ,这样子一条未定向就被我们转化成了谁的入度加 (1) ,直接二分图匹配就可以了, (xchoose2) 差分建边就可以了。

    Code

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #define fi first
    #define se second
    const int N = 100;
    using namespace std;
    int n,a[N + 5][N + 5],S,T,idc,id[N + 5],d[N + 5],t[N + 5],ans,id2[N + 5][N + 5];
    namespace F
    {
    	const int N = 3e6;
    	const long long inf = 2e18;
    	#define mp make_pair
    	struct edges
    	{
    		int v;
    		long long w,f;
    	}edge[N + 5];
    	int head[N + 5],S,T,nxt[N + 5],edge_cnt = 1,cur[N + 5],vis[N + 5],p[N + 5],q[N + 5];
    	long long dis[N + 5],cost;
    	void add_edge(int u,int v,long long w,long long f)
    	{
    		edge[++edge_cnt] = (edges){v,w,f};
    		nxt[edge_cnt] = head[u];
    		head[u] = edge_cnt;
    	}
    	void add(int u,int v,long long w,long long f)
    	{
    		add_edge(u,v,w,f);
    		add_edge(v,u,0,-f);
    	}
    	bool spfa()
    	{
    		for (int i = 1;i <= idc;i++)
    			dis[i] = inf,cur[i] = head[i],vis[i] = p[i] = 0;
    		int l = 1,r = 0;
    		dis[S] = 0;
    		q[++r] = S;
    		while (l <= r)
    		{
    			int u = q[l++];
    			vis[u] = 0;
    			for (int i = head[u];i;i = nxt[i])
    			{
    				int v = edge[i].v;
    				long long w = edge[i].w,f = edge[i].f;
    				if (w && dis[v] > dis[u] + f)
    				{
    					dis[v] = dis[u] + f;
    					if (!vis[v])
    					{
    						q[++r] = v;
    						vis[v] = 1;
    					}
    				}
    			}
    		}
    		return dis[T] != inf;
    	}
    	long long dfs(int u,long long flow)
    	{
    		if (u == T)
    			return flow;
    		long long sm = 0;
    		p[u] = 1;
    		for (int &i = cur[u];i;i = nxt[i])
    		{
    			int v = edge[i].v;
    			long long w = edge[i].w,f = edge[i].f;
    			if (w && dis[v] == dis[u] + f && !p[v])
    			{
    				long long res = dfs(v,min(flow,w));
    				edge[i].w -= res;
    				edge[i ^ 1].w += res;
    				flow -= res;
    				sm += res;
    				cost += res * f;
    				if (!flow)
    					break;
    			}
    		}
    		p[u] = 0;
    		return sm;
    	}
    	pair <long long,long long> dinic(int s,int t)
    	{
    		S = s;T = t;
    		long long ans = 0;
    		cost = 0;
    		while (spfa())
    			ans += dfs(S,inf);
    		return mp(ans,cost);
    	}
    	void clear()
    	{
    		for (int i = 1;i <= idc;i++)
    			head[i] = 0;
    		edge_cnt = 1;
    		idc = 0;
    	}
    	void solve()
    	{
    		for (int i = 1;i <= n;i++)
    			for (int j = i + 1;j <= n;j++)
    				if (a[i][j] == 2)
    				{
    					for (int k = head[id2[i][j]];k;k = nxt[k])
    					{
    						int v = edge[k].v,w = edge[k].w;
    						if (v != S && !w)
    						{
    							if (id[j] == v)
    								a[i][j] = 1,a[j][i] = 0;
    							else
    								a[i][j] = 0,a[j][i] = 1;
    						}
    					}
    				}
    	}
    }
    int C2(int x)
    {
    	return x * (x - 1) / 2;
    }
    int C3(int x)
    {
    	return x * (x - 1) * (x - 2) / 6;
    }
    int main()
    {
    	scanf("%d",&n);
    	for (int i = 1;i <= n;i++)
    		for(int j = 1;j <= n;j++)
    			scanf("%d",&a[i][j]);
    	for (int i = 1;i <= n;i++)
    		for (int j = i + 1;j <= n;j++)
    		{
    			if (a[i][j] == 1)
    				d[j]++,t[j]++;
    			if (a[i][j] == 0)
    				d[i]++,t[i]++;
    			if (a[i][j] == 2)
    				t[i]++,t[j]++;
    		}
    	S = ++idc;T = ++idc;
    	for (int i = 1;i <= n;i++)
    	{
    		id[i] = ++idc;
    		for (int j = d[i] + 1;j <= t[i];j++)
    			F::add(id[i],T,1,C2(j) - C2(j - 1));
    		ans += C2(d[i]);
    	}
    	for (int i = 1;i <= n;i++)
    		for (int j = i + 1;j <= n;j++)
    			if (a[i][j] == 2)
    			{
    				id2[i][j] = ++idc;
    				F::add(S,idc,1,0);
    				F::add(idc,id[i],1,0);
    				F::add(idc,id[j],1,0);
    			}
    	ans = C3(n) - (F::dinic(S,T).se + ans);
    	cout<<ans<<endl;
    	F::solve();
    	for (int i = 1;i <= n;i++)
    	{
    		for (int j = 1;j <= n;j++)
    			printf("%d ",a[i][j]);
    		putchar(10);
    	}
    	return 0;
    }
    
  • 相关阅读:
    NSLocalizedString用法
    4-27学习心得
    手势学习
    plist处理
    数据存储
    initWithFrame方法
    控制器跳转小常识
    UIGestureRecognizer学习笔记
    博客资源
    检测手机wifi有没有打开
  • 原文地址:https://www.cnblogs.com/sdlang/p/14599766.html
Copyright © 2011-2022 走看看