Chiaki has a positive integer m and she would like to construct a tree with at most 15 vertices in such a manner that the number of distinct nonempty independent sets is exactly m.
Note that an independent set is a subset of vertices of a graph such that every two distinct vertices are not adjacent.
Input
There are multiple test cases. The first line of input contains an integer T (1 ≤ T ≤ 2000), indicating the number of test cases. For each test case:
The first line contains an integer m (1 ≤ m ≤ 2000).
Output
For each test case, if there is no such graph, output -1 on a single line. Otherwise, output an integer n (1 ≤ n ≤ 15) denoting the number of vertices. Then in each of the next n - 1 lines, output two integers x and y (1 ≤ x, y ≤ n, x ≠ y) denoting an edge in the tree.
Sample Input
5 1 2 3 10 20
Sample Output
1 2 2 1 -1 -1 6 2 1 3 1 4 3 5 4 6 5
题解:
- /*
- 【trick&&吐槽】
- 1,csy还是套路深啊
- 2,状态太复杂的还是用下标表示得好
- 3,我们四重循环,实际复杂度却并不高,但是要用对称DP来实现无缝的值域覆盖
- 【题意】
- 我们最多使用15个节点,构造出一棵树,使得——这棵树中恰好有m个不同的集合{}
- 对于其中任意一个集合,其必须要是个独立集,即:其里面的任意两个点之间没有直接边相连。
- 【分析】
- 我们假设自己已经知道了树形态,思考如何求出独立集。
- 这个可以是2 ^ n * n枚举,当然最好的方法是树形DP。
- 这是一棵树,所以独立集的判定其实较为简单一点。
- 同时,独立集的转移也比较简单,我们只要考虑一个点取与不取两种情况。
- 于是,我们可以定义状态——
- f[i][j]表示,如果我们选取根节点,会有i个独立集;如果我们不选取根节点,会有j个独立集,在这种条件下对应的最少节点数。
- 为什么定义出这么一个状态来呢?
- 因为,我们考虑独立集的计数时,是要求没有相邻点的。
- 于是,需要知道一个点选或不选对应的独立集个数。而这个点其实对应于一个根。
- 即:我们每次操作的时候必然基于一个点,就不妨把这个点设置为根。
- 我们知道了f[i][j]与f[x][y]这样两个树形态,我们考虑合并。
- 当然合并的对象是两个子树的根,合并之后的根可能是f[i][j]的根,也可能是f[x][y]的根。
- 假如合并之后的根是f[i][j]的根,与f[x][y]做合并,变成了f[i * y][j * (x + y)];
- 假如合并之后的根是f[x][y]的根,与f[i][j]做合并,变成了f[x * j][y * (i + j)];
- 初始状态是什么呢?f[1][1] = 1. 即我们把0个点选取的独立集也考虑,这样才使得合并计数准确。至于输入的m,使得m += 1即可。
- 【时间复杂度&&优化】
- Compute Time = 206561
-
感觉聚聚写的已经很好了,就直接拿来用了附上链接:http://blog.csdn.net/snowy_smile/article/details/70148735
代码:
#include <iostream>
#include <bits/stdc++.h>
using namespace std;
struct node
{
short i,j,x,y;
}pp[2005][2005];
int dp[2005][2005];
void solve()
{
dp[1][1]=1;
for(int i=1;i<=2000;i++)
{
for(int j=1;j+i<=2000;j++)if(dp[i][j]<=15)
{
for(int y=1;i*y<=2000;y++)
{
for(int x=1;j*(x+y)<=2000;x++)
{
if(dp[i][j]+dp[x][y]<=15)
{
int tep=dp[i][j]+dp[x][y];
if(dp[i*y][j*(x+y)]>tep)
{
dp[i*y][j*(x+y)]=tep;
pp[i*y][j*(x+y)].i=(short)i;
pp[i*y][j*(x+y)].j=(short)j;
pp[i*y][j*(x+y)].x=(short)x;
pp[i*y][j*(x+y)].y=(short)y;
}
}
}
}
for(int x=1;x*j<=2000;x++)
{
for(int y=1;y*(i+j)<=2000;y++)
{
if(dp[i][j]+dp[x][y]<=15)
{
int tep=dp[i][j]+dp[x][y];
if(dp[x*j][y*(i+j)]>tep)
{
dp[j*x][y*(i+j)]=tep;
pp[j*x][y*(i+j)].i=(short)x;
pp[j*x][y*(i+j)].j=(short)y;
pp[j*x][y*(i+j)].x=(short)i;
pp[j*x][y*(i+j)].y=(short)j;
}
}
}
}
}
}
}
int cnt1=1;
void print(int rt, int x, int y)
{
if (x == 1 && y == 1)return;
print(rt, pp[x][y].i, pp[x][y].j);
printf("%d %d\n", rt, ++cnt1);
print(cnt1, pp[x][y].x, pp[x][y].y);
}
int main()
{
//freopen("1.in","r",stdin);
for(int i=1;i<=2000;i++)
for(int j=1;j<=2000;j++)
dp[i][j]=16;
//int a=clock();
solve();
//int b=clock();
//cout<<a<<' '<<b;
int t;
cin>>t;
while(t--)
{
int m;
cnt1=1;
scanf("%d",&m);
m+=1;
int min1;
int ans1=16;
for(int i=1;i<m;i++)
{
if(dp[i][m-i]<ans1){
ans1=dp[i][m-i];
min1=i;
break;
}
}
if(ans1==16)
printf("-1\n");
else{
printf("%d\n",ans1);
print(1,min1,m-min1);
}
}
//cout << "Hello world!" << endl;
return 0;
}