题目:魔术球问题
思路:
听说这题答案的范围很小。
听说从1开始枚举答案大小不会超时。
然后我就枚举答案了。
假设答案为m,就对于m个数,每个数暴力的寻找可以可以放在它下面的数再建边。
由于只有可能上面的点指向下面的点,所以这张图不存在环,可见是一张二分图。
然后从m开始跑一遍匈牙利算法看看能否求出增广路,如果不能,则说明无法对m进行匹配,则需要再加一根柱子放m。
当柱子的数量大于n时,就说明m-1即为答案。
然后顺着match数组找路径就好了。
代码:
#include<bits/stdc++.h>
using namespace std;
#define maxn 55
#define maxm 2000
#define m maxm
#define read(x) scanf("%d",&x)
int n;
bool isq[maxm*maxm+5];
vector<int> a[maxm+5];
bool use[maxm+5];
int match[maxm+5];
void init() {
for(int i=1;i<=m;i++) {
isq[i*i]=true;
}
}
void add(int x) {
for(int i=1;i<x;i++) {
if(isq[i+x]) a[x].push_back(i);
}
}
bool dfs(int x) {
if(use[x]) return false;
use[x]=true;
for(int i=0;i<a[x].size();i++) {
int y=a[x][i];
if(!match[y]||dfs(match[y])) {
match[y]=x;
return true;
}
}
return false;
}
void print(int x) {
while(x) {
printf("%d ",x);
use[x]=true;
x=match[x];
}
printf("\n");
}
int main() {
read(n);
init();
int nxt=0;
for(int i=0;i<=n;i++) {
for(int j=nxt+1;j<=m;j++) {
add(j);
if(!dfs(j)) {
nxt=j;
break;
}
memset(use,0,sizeof(use));
}
}
nxt--;
printf("%d\n",nxt);
for(int i=1;i<=nxt;i++) {
if(use[i]) continue;
print(i);
}
return 0;
}