zoj3951 Independent Set(树形dp,空间优化)

Independent Set

Time Limit: 1 Second       Memory Limit: 65536 KB       Special Judge

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 ≤ xy ≤ nx ≠ 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

题解:

  1. /* 
  2. 【trick&&吐槽】 
  3. 1,csy还是套路深啊 
  4. 2,状态太复杂的还是用下标表示得好 
  5. 3,我们四重循环,实际复杂度却并不高,但是要用对称DP来实现无缝的值域覆盖 
  6.  
  7. 【题意】 
  8. 我们最多使用15个节点,构造出一棵树,使得——这棵树中恰好有m个不同的集合{} 
  9. 对于其中任意一个集合,其必须要是个独立集,即:其里面的任意两个点之间没有直接边相连。 
  10.  
  11. 【分析】 
  12. 我们假设自己已经知道了树形态,思考如何求出独立集。 
  13. 这个可以是2 ^ n * n枚举,当然最好的方法是树形DP。 
  14. 这是一棵树,所以独立集的判定其实较为简单一点。 
  15. 同时,独立集的转移也比较简单,我们只要考虑一个点取与不取两种情况。 
  16.  
  17. 于是,我们可以定义状态—— 
  18. f[i][j]表示,如果我们选取根节点,会有i个独立集;如果我们不选取根节点,会有j个独立集,在这种条件下对应的最少节点数。 
  19.  
  20. 为什么定义出这么一个状态来呢? 
  21. 因为,我们考虑独立集的计数时,是要求没有相邻点的。 
  22. 于是,需要知道一个点选或不选对应的独立集个数。而这个点其实对应于一个根。 
  23. 即:我们每次操作的时候必然基于一个点,就不妨把这个点设置为根。 
  24. 我们知道了f[i][j]与f[x][y]这样两个树形态,我们考虑合并。 
  25. 当然合并的对象是两个子树的根,合并之后的根可能是f[i][j]的根,也可能是f[x][y]的根。 
  26.  
  27. 假如合并之后的根是f[i][j]的根,与f[x][y]做合并,变成了f[i * y][j * (x + y)]; 
  28. 假如合并之后的根是f[x][y]的根,与f[i][j]做合并,变成了f[x * j][y * (i + j)]; 
  29.  
  30. 初始状态是什么呢?f[1][1] = 1. 即我们把0个点选取的独立集也考虑,这样才使得合并计数准确。至于输入的m,使得m += 1即可。 
  31.  
  32. 【时间复杂度&&优化】 
  33. Compute Time = 206561 
  34.  

感觉聚聚写的已经很好了,就直接拿来用了附上链接: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;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值