此题略坑。。。
思路:把N个点分成m若干个联通子图,然后用m-1个桥把这m个联通子图连接起来即可。
若每个联通子图内部都是完全图也符合题意,但答案却是Wrong Answer,只有把每个联通子图内部当成环来输出才通过,这是本题的一个坑,一个Bug!!!
另外联通子图中点的个数不能等于2(如果不明白请读者认真思考5遍)
第三个要注意的地方就是输出时每个联通子图只能有一个点与外界连接,不能有两个!!!(如果不明白请读者认真思考10遍)
关键在于确定每个联通子图中点的个数,若第i个联通子图中的点个数为a[i],则Σa[i]=N,且Σ(a[i]*(n-a[i]))=2K
即Σa[i]2 =N2 - 2K ,Σa[i]=N
方法有两种:DP 和 搜索
DP:
set<int>s[105];
s[i]是和为i的若干个数的平方和的集合。
dp[105][10005]
若干个数和为i,平方和为j,则dp[i][j]为这些数中最大的那个
s[0].insert(0); for (int i=0;i<100;++i) for (set<int>::iterator it=s[i].begin();it!=s[i].end();++it) for (int j=i?dp[i][*it]:1;j<=100-i;++j) { if (j==2) continue; s[i+j].insert((*it)+j*j); dp[i+j][(*it)+j*j]=j; }
搜索:
找出一组数,和为N,平方和为N2 - 2K :
若找到了这样的一组数,返回数的个数,否则返回0
int check(int x,int n,int depth) { if (n*n<x) return 0; for (int i=(depth>1)?a[depth-1]:1;i<=n && x-i*i>=n-i;++i) { if (i==2) continue; a[depth]=i; if (i==n && i*i==x) return depth; int y=check(x-i*i,n-i,depth+1); if (y) return y; } return 0; }
完整代码如下:
DP版:
/************************************************************************* > File Name: D.cpp > Author: Chierush > Mail: [email protected] > Created Time: 2013年08月02日 星期五 09时05分46秒 ************************************************************************/ #include <iostream> #include <cstring> #include <cstdlib> #include <set> #include <cstdio> #include <string> #include <vector> #include <map> #include <cmath> #include <algorithm> #define LL long long #define LLU unsigned long long using namespace std; int a[101],cnt; int dp[105][10005]; set<int>s[105]; int main() { s[0].insert(0); for (int i=0;i<100;++i) for (set<int>::iterator it=s[i].begin();it!=s[i].end();++it) for (int j=i?dp[i][*it]:1;j<=100-i;++j) { if (j==2) continue; s[i+j].insert((*it)+j*j); dp[i+j][(*it)+j*j]=j; } int n,k; scanf("%d%d",&n,&k); int x=n*n-2*k; if (x<n) { puts("-1"); return 0; } if (!dp[n][x]) puts("-1"); else { int p=n,q=x; for (;;) { if (!p || !q) break; int o=dp[p][q]; a[cnt++]=o; p-=o,q-=o*o; } sort(a,a+cnt); for (int i=1;i<cnt;++i) a[i]+=a[i-1]; for (int i=1;i<a[0];++i) printf("%d %d\n",i,i+1); if (a[0]>1) printf("%d %d\n",1,a[0]); for (int i=1;i<cnt;++i) { printf("%d %d\n",a[i-1],a[i]); for (int j=a[i-1]+1;j<a[i];++j) printf("%d %d\n",j,j+1); if (a[i]-a[i-1]>1) printf("%d %d\n",a[i-1]+1,a[i]); } } return 0; }
搜索版:
/************************************************************************* > File Name: D.cpp > Author: Chierush > Mail: [email protected] > Created Time: 2013年08月02日 星期五 09时05分46秒 ************************************************************************/ #include <iostream> #include <cstring> #include <cstdlib> #include <set> #include <cstdio> #include <string> #include <vector> #include <map> #include <cmath> #include <algorithm> #define LL long long #define LLU unsigned long long using namespace std; int a[101],cnt; int check(int x,int n,int depth) { if (n*n<x) return 0; for (int i=(depth>1)?a[depth-1]:1;i<=n && x-i*i>=n-i;++i) { if (i==2) continue; a[depth]=i; if (i==n && i*i==x) return depth; int y=check(x-i*i,n-i,depth+1); if (y) return y; } return 0; } int main() { int n,k; scanf("%d%d",&n,&k); int x=n*n-2*k; if (x<n) { puts("-1"); return 0; } int u=check(x,n,1); if (!u) puts("-1"); else { for (int i=2;i<=u;++i) a[i]+=a[i-1]; for (int i=1;i<a[1];++i) printf("%d %d\n",i,i+1); if (a[1]>1) printf("%d %d\n",1,a[1]); for (int i=2;i<=u;++i) { printf("%d %d\n",a[i-1],a[i]); for (int j=a[i-1]+1;j<a[i];++j) printf("%d %d\n",j,j+1); if (a[i]-a[i-1]>1) printf("%d %d\n",a[i-1]+1,a[i]); } } return 0; }