P2765 魔术球问题&&二分图

题意

here

思路

1

根据此题输入m的范围,可以知道此题的答案上限约为5000
考虑逆向二分求解(实际上可以直接枚举)

2

此题可以抽象成在图上求最少链的个数
我们把所有数向比他大的、与他的和为平方数的数建边
可以看出是二分图最大匹配问题
结合图更清晰:

此时图上最少链的个数为
\(n\)(点数)\(- ans\)(最大匹配数)\(6-3=3\)
这三条链分别为:
1 3 6
4 5
2
可以看作从 \(1\) 开始,不断在从左边跳到右边

  • 左边的 1 连接了右边的 3,这条链上有\(1 3\)。3已经被加到一条链中,标记一下,再遍历到 3 时直接跳过
    左边的 3 又连接到了右边的 6,这条链就变成了\(1 3 6\) 标记 6
  • 左边的 2 没有连接右边的点,这条链上只有 \(2\)
  • 遍历到 3,3 已经被标记,continue
  • 4 连接了 5,标记 5,此时这条链变为 \(45\)
  • 5 被标记,continue
  • 6 被标记,结束。

代码:

#include<bits/stdc++.h>

using namespace std;

const int maxn=5005;

int m;
vector<int>g[maxn*2];
int result[maxn*2];
int mm[maxn*2];
bool vis[maxn*2];

bool dfs(int p)
{
	for(int j=0;j<g[p].size();j++)
	{
		if(!vis[g[p][j]]){
			vis[g[p][j]]=true;
			if(result[g[p][j]]==0||dfs(result[g[p][j]]))
			{
				result[g[p][j]]=p;
				mm[p]=g[p][j];
				return true;
			}
		}
	}
	return false;
}

bool is_sqre(int p)
{
    int t = sqrt(p); 
    if(t * t == p){
        return true;
    }
    return false;
}

bool check(int n)
{
	memset(mm,0,sizeof(mm));
	int ans=0;
	for(int i=1;i<=n;i++)
		g[i].clear();
	memset(result,0,sizeof(result));
	for(int i=1;i<=n;i++)
	{
		for(int j=i+1;j<=n;j++)
		{
			if(is_sqre(i+j)) g[i].push_back(j);
		}
	}
	for(int i=1;i<=n;i++)
	{
		if(dfs(i)) ans++;
		memset(vis,false,sizeof(vis));
	}
	if(n-ans>m) return false;//需要更多的柱子 
	else return true;
}

int main()
{
	cin>>m;
	int l=1,r=5000;
	while(l<r){
		int mid=(l+r)>>1;
		if(check(mid)) l=mid+1;
		else r=mid;
	}
	cout<<l-1<<endl;
	for(int j=1;j<=l-1;j++)
	{
		if(!vis[j])
		{	
			for(int i=j;i;i=mm[i])
			{
				cout<<i<<" ";
				vis[i]=true;
			}
			cout<<endl;
		}
	}
	return 0;
} 
posted @ 2024-12-29 17:36  lazy_ZJY  阅读(3)  评论(0编辑  收藏  举报