[网络流24题 #4]魔术球问题

本来是应该用最大流做的的
但是实际上还有一种方法可以得到答案:
现在如果有n个柱子,要放数i,从第一个柱子开始试,试到能放的那个柱子,就把i放进去,如果n个柱子都不行,就停止计算,得到最大值。
这其实是一个贪心的证明,事实证明最大流与贪心的答案是吻合的。同样这是一个方案数多样化的题目,鉴于没有Special Judge,所以过不了也是正常,不过不用管它。
这道题的标准做法应该是最小路径覆盖,做成一个二分图,进而求最大流。
下面copy一下ByVoid大神的分析:

【建模方法】
枚举答案A,在图中建立节点1..A。如果对于i<j有i+j为一个完全平方数,连接一条有向边(i,j)。该图是有向无环图,求最小路径覆盖。如果刚好满足最小路径覆盖数等于N,那么A是一个可行解,在所有可行解中找到最大的A,即为最优解。
具体方法可以顺序枚举A的值,当最小路径覆盖数刚好大于N时终止,A-1就是最优解。
【建模分析】
由于是顺序放球,每根柱子上的球满足这样的特征,即下面的球编号小于上面球的编号。抽象成图论,把每个球看作一个顶点,就是编号较小的顶点向编号较大的顶点连接边,条件是两个球可以相邻,即编号之和为完全平方数。每根柱子看做一条路径,N根柱子要覆盖掉所有点,一个解就是一个路径覆盖。
最小路径覆盖数随球的数量递增不递减,满足单调性,所以可以枚举答案(或二分答案),对于特定的答案求出最小路径覆盖数,一个可行解就是最小路径覆盖数等于N的答案,求出最大的可行解就是最优解。本问题更适合枚举答案而不是二分答案,因为如果顺序枚举答案,每次只需要在残量网络上增加新的节点和边,再增广一次即可。如果二分答案,就需要每次重新建图,大大增加了时间复杂度。

然后放出贪心代码,其实比最大流短得多:

#include <iostream>
#include <vector>
#include <cmath>
#define MaxN 60
using namespace std;
vector<int> a[MaxN];
inline bool suit(const int &x,const int &y)
{
	int n=x+y;
	return !(int(sqrt(n))<sqrt(n));
}
int main()
{
	freopen("ball.in","r",stdin);
	freopen("ball.out","w",stdout);
	int n,ans=1;
	bool flag;
	cin>>n;
	for(int i=1;i<=n;i++)
		a[i].push_back(0);
	for(;;)
	{
		flag=0;
		for(int i=1;i<=n;i++)
			if(suit(ans,a[i][a[i][0]])||!a[i][0])
			{
				flag=1,
				a[i][0]++,
				a[i].push_back(ans),
				ans++;
				break;		
			}
		if(!flag) break;
	}
	cout<<ans-1<<endl;
	for(int i=1;i<=n;i++,printf("\n"))
		for(int j=1;j<=a[i][0];j++)
			printf("%d ",a[i][j]);
	return 0;
}
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值