题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4982
大致题意:题目首先给定了一个包含k个不同的正整数的集合,又定义了一个平方集合,一个数成为平方集合的条件有两个:
1.集合中k个数的和等于n
2.存在一个包含k-1个数的子集,满足这k-1个数的和等于一个数的平方(这个数可以任意取)
输入n,k问是否存在一个符合n,k的平方集合。
思路:
根据题目条件我们能很快的列出来两个方程:
1. a1+a2+a3+.......+ak=n
2. a1+a2+a3+.......+ak-1=x*x
所以有: 3.x*x=n-ak 此题的精髓就是集合包含的这k个数必须都是不同的。
根据3的公式我们可以把原问题转化成,x*x是否能由k-1个不含ak的数组成,比赛的时候就想到了这个地方的就进行不下去了。后来想到的方法就是先构造出k-1数使他们的和等于x*x的平方,再以这个解为基础看能否将其变化成满足题意的状态,如果可以则表示存在,如果不可以则表示不存在。
在构造的时候我们先让前k-2个数尽可能的小,我将它们取成1,2,3,4,.........,k-2,再将第k-1个数取成x*x与这k-2个数的差,如果ak-1是在1~ak-2之间的话,则不存在解,
如果ak>ak-2且ak不等于ak-1,则我们现在构建的这组解就是满足题意的。 如果ak<=ak-2则我们就需要来构建这组解。
构建解的方法是,将与ak相同的数取出,将其换成尽可能小的数,因为我们已经将前k-2个数取完了,所以这个最小的数只可能是k-1,计算出这个数的与ak的差作为增量,因为前ak-2个数都是相邻的,所以减少量只能由ak-1来提供,这是我们就判断一下ak-1能否提供这个与增量相同的减少量即可~~~~~
由于这种题也没有什么算法,我称作此类题为想法题~
code:
#include <iostream>
#include <cstring>
#include <cstdlib>
#include<cstdio>
#include <queue>
#include <set>
#include <vector>
#include <cmath>
#include <algorithm>
#define INF 2000000000
using namespace std;
typedef long long ll;
const int MAX_E=10000;
const int MAX_N=1005;
const int MAX_K=3000;
const int MAXN=10010;
bool judge(int sum,int y,int k)
{
int mid1=(k-1)*k/2;
int mid2=sum-mid1;
if(mid2<=k-1) return false;
if(y>k-1&&y!=mid2) return true;
if(y<=k-1){
if(k-y<=mid2-(k+1)) return true;
else return false;
}
if(y==mid2){
if(mid2-(k-1)>=3) return true;
else return false;
}
}
int main()
{
int n,k,T,mm;
bool flag;
while(scanf("%d%d",&n,&k)!=EOF){
int c1=(int)sqrt((double)k*(k-1)/2);
int c2=(int)sqrt((double)n);
flag=0;
for(int i=c1;i<=c2;i++){
if((k*(k-1)/2<=i*i)&&(i*i<=n-1)&&judge(i*i,n-i*i,k-1)){
flag=1;
break;
}
}
if(flag==1) printf("YES\n");
else printf("NO\n");
}
return 0;
}