所谓的日常 #3 - 議溫明董卓叱丁原 饋金珠李肅說呂布
div.2
嘛...看起来大家之前做过这个题。至于原来ac的代码在这里交不能过,大概因为这里的数据更强一些吧。
1 #include <stdio.h> 2 3 int a[10000],tot; 4 5 void work(int n,int m) { 6 tot = 0; 7 if (n == 0) { 8 a[tot++] = 0; 9 } else { 10 while (n) { 11 a[tot++] = n % m; 12 n /= m; 13 } 14 } 15 for (int i = tot - 1; i >= 0; -- i) { 16 if (a[i] >= 10) 17 putchar(a[i] - 10 + 'A'); 18 else 19 printf("%d",a[i]); 20 } 21 puts(""); 22 } 23 24 int main() { 25 int n,m; 26 while (scanf("%d%d",&n,&m) == 2) { 27 work(n,m); 28 } 29 }
div.1
给定二次元平面上n(<=16)个点,用直线把他们全部覆盖。问最少要几根直线。
那么 这是一道状态压缩dp。(不知道这是啥的出门左转学一下?,一言蔽之,就是dp[mask],mask为一个集合,mask的二进制位第i位为0表示没有i,1表示有i。或者可以这么说,你本要开dp[2][2][2][2][2]这么个状态,然而其实只要开dp[2 ^ 5]就好了)
然后大家知道 两个点确定一条直线。所以转移是枚举两个点,把这根直线覆盖的点们更新到状态里。这样的复杂度是O(n^3 * 2^n),所以我交的第一发TLE了。
考虑预处理。预处理出数组cover(i,j)表示选择i,j两个点构成的线段能够覆盖的点的集合。预处理O(n^3),dp部分O(n^2 * 2^n),又TLE了。
再次优化,由于每个点最终其实都被某根直线覆盖了,所以我们每次不需要枚举两个点,只需要枚举一个点,把它和当前未被覆盖的标号最小的点连起来就可以了。同样预处理O(n^3),dp部分O(n * 2^n),Accepted。
然后,判断3点共线,使用叉积即可,也就是代码里的det()函数。
然后...代码里有一些位运算,关于位运算的优先级问题 可以百度一下,有张表。但是建议如果搞不清运算顺序的话 就加括号吧。
1 #include <stdio.h> 2 3 const int N = 16; 4 5 struct Point { 6 Point() {} 7 Point(int _x,int _y) : x(_x),y(_y) {} 8 Point operator - (const Point &rhs) const { 9 return Point(x - rhs.x,y - rhs.y); 10 } 11 int x,y; 12 }; 13 14 int det(const Point &a,const Point &b) { 15 return a.x * b.y - a.y * b.x; 16 } 17 18 Point points[N]; 19 20 const int INF = 0x3f3f3f3f; 21 int dp[1 << N]; 22 int n; 23 int cover[N][N]; 24 25 inline void update(int &a,int b) { 26 if (a > b) a = b; 27 } 28 29 int work() { 30 for (int mask = 0; mask < 1 << n; ++ mask) { 31 dp[mask] = INF; 32 } 33 for (int i = 0; i < n; ++ i) { 34 for (int j = i + 1; j < n; ++ j) { 35 int mask = 0; 36 for (int k = 0; k < n; ++ k) { 37 if (det(points[i] - points[k],points[j] - points[k]) == 0) 38 mask |= 1 << k; 39 } 40 cover[i][j] = mask; 41 } 42 } 43 dp[0] = 0; 44 for (int mask = 0; mask < 1 << n; ++ mask) { 45 int x = -1; 46 for (int i = 0; i < n; ++ i) { 47 if (!(mask >> i & 1)) { 48 x = i; 49 break; 50 } 51 } 52 if (x == -1) continue; 53 update(dp[mask ^ 1 << x],dp[mask] + 1); 54 for (int i = x + 1; i < n; ++ i) { 55 update(dp[mask | cover[x][i]],dp[mask] + 1); 56 } 57 } 58 return dp[(1 << n) - 1]; 59 } 60 61 int main() { 62 int cas,ca = 0; 63 scanf("%d",&cas); 64 while (cas--) { 65 scanf("%d",&n); 66 for (int i = 0; i < n; ++ i) { 67 scanf("%d%d",&points[i].x,&points[i].y); 68 } 69 printf("Case %d: %d\n",++ca,work()); 70 } 71 }