P2765 魔术球问题&&二分图

题意

here

思路

1

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

2

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

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

  • 左边的 1 连接了右边的 3,这条链上有13。3已经被加到一条链中,标记一下,再遍历到 3 时直接跳过
    左边的 3 又连接到了右边的 6,这条链就变成了136 标记 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 @   lazy_ZJY  阅读(6)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示