uva 11008(状态压缩+记忆化搜索)

这题属于状态压缩DP中比较基础的一题,经过仔细分析后我们发现此题虽然坐标范围较大,但是点比较少最多才16个很容易想到用状态压缩。dp[x]表示当前树的状态最少要转移的次数(砍的次数)。具体状态转移由于他状态转移的顺序比较乱所以用的是记忆化搜索。起始状态是(1<<n)-1,边界是已有树的数目小于n-m(kk)返回0,只多出一棵树是返回1。

代码如下:

 1 #include <iostream>
 2 #include <stdio.h>
 3 #include <string.h>
 4 using namespace std;
 5 
 6 int n, kk, ans, dp[1000000];      //状态压缩数组一共2^16个状态
 7 
 8 typedef struct Point{
 9     int x, y;
10 }P;
11 
12 P p[20];
13 
14 //判断三点是否共线
15 bool isline(int i,int j,int k)
16 {
17     int a=p[i].x-p[j].x,b=p[i].y-p[j].y;
18     int c=p[j].x-p[k].x,d=p[j].y-p[k].y;
19     return a*d==b*c;
20 }
21 
22 
23 int Judge(int x)
24 {
25     int ret = 0;
26     while(x){
27         ret+=(x&1);
28         x>>=1;
29     }
30     return ret;
31 }
32 
33 void init()
34 {
35     scanf("%d%d", &n, &kk);
36     for(int i=0; i<n; i++){
37         scanf("%d%d", &p[i].x, &p[i].y);
38     }
39     ans = 20;
40     memset(dp, -1, sizeof dp);
41 }
42 
43 int solve(int x)
44 {
45     if(dp[x]!=-1) return dp[x];
46     if(Judge(x)<=n-kk) return 0;
47     if(Judge(x)==n-kk+1) return dp[x] = 1;
48 
49     int ret = 20;
50     for(int i=0; i<n; i++)
51     if((1<<i)&x){
52         for(int j=i+1; j<n; j++)
53         {
54             if((1<<j)&x){   //两棵树都在枚举直线
55                 int tx = x;
56                 for(int k=0; k<n; k++){
57                     if((1<<k)&x && isline(i, j, k)){
58                         tx -= (1<<k);
59                     }
60 
61                 }
62                 ret = min(ret, solve(tx)+1);
63             }
64         }
65     }
66     return dp[x] = ret;
67 }
68 
69 int main()
70 {
71     //freopen("in.txt","r", stdin);
72     //freopen("out.txt","w", stdout);
73 
74 
75     int T, cnt;
76     scanf("%d", &T);
77     for(cnt=1; cnt<=T; cnt++)
78     {
79         init();
80         ans = solve((1<<n)-1);
81         printf("Case #%d:
", cnt);
82         printf("%d
", ans);
83         if(cnt!=T) printf("
");
84     }
85     return 0;
86 }
奔跑吧!少年!趁着你还年轻
原文地址:https://www.cnblogs.com/shu-xiaohao/p/3373874.html