UVA 11134 Fabled Rooks(贪心的妙用+memset误用警示)
题目链接:
https://cn.vjudge.net/problem/UVA-11134
1 /* 2 问题 输入棋盘的规模和车的数量n(1=<n<=5000),接着输入n辆车的所能在的矩阵的范围,计算并输出使得每辆车横竖都不能相互攻击 3 的摆放方法,能则输出每辆车的坐标,不能则输出"IMPOSSIBLE"。 4 解题思路 想想怎么将问题分解成几个小问题,不同行不同列的话,将其分成两个一维问题,采用DFS向下搜索,搜的时候注意每个车的 5 行区间和列区间,找到一种则直接返回,输出对应每辆车的行和列即可。但是超时!!! 6 使用贪心法,先将所有区间按照右端点的大小进行排序,然后枚举每一个区间,找到一个数k,满足在此区间内,直到所有区间都找到 7 一个数输出结果或者其中有一个车在其区间内找不到合适的位置则输出IMPOSSIBLE 8 */ 9 #include<cstdio> 10 #include<cstring> 11 const int N = 5050; 12 13 int xl[N],xr[N],yl[N],yr[N]; 14 int n; 15 int row[N],col[N]; 16 17 int solve(int ans[],int l[],int r[]); 18 19 int main() 20 { 21 int i; 22 while(scanf("%d",&n), n != 0){ 23 for(i=0;i<n;i++){ 24 scanf("%d%d%d%d",&xl[i],&yl[i],&xr[i],&yr[i]); 25 } 26 27 if(solve(row,xl,xr) && solve(col,yl,yr)){ 28 for(i=0;i<n;i++){ 29 printf("%d %d\n",row[i],col[i]); 30 } 31 } 32 else 33 printf("IMPOSSIBLE\n"); 34 } 35 return 0; 36 } 37 38 int solve(int ans[],int l[],int r[]) 39 { 40 int cur,minr;//minr为包含k的区间最小右界(刚开始时初始化为最大,便于寻找最小),cur为放k的最优区间(号) 41 memset(ans,-1,sizeof(int)*n); 42 //memset(ans,-1,sizeof(ans));错误用法,详见分析 43 int k,i; 44 for(k=1;k<=n;k++){ 45 cur = -1,minr = N;//初始化刚开始时初始化为最大,便于寻找最小 46 for(i=0;i<n;i++){//枚举每个区间 47 if(ans[i] < 0 && l[i] <= k && r[i] < minr){ 48 //该区间没有被用过且k大于等于该区间的左边界且最小右边界也在该区间内 49 cur = i;//更新 预备将k存放的区间号 50 minr = r[i];//缩小右边界,也是贪心法的体现,总是放在可行区间的最右侧 51 } 52 } 53 //没有区间能够放置k或者k不满足在最优区间内 54 if(cur < 0 || k > minr) return 0; 55 //将k放置在cur区间内 56 ans[cur]=k; 57 } 58 return 1; 59 } 60 61 /*DFS搜索,超时!!! 62 #include<cstdio> 63 #include<cstring> 64 struct REC{ 65 int xr,yr,xl,yl; 66 }rec[5050]; 67 68 int row[5050],col[5050],n,flag1,flag2; 69 int bkrow[5050],bkcol[5050]; 70 71 void dfsrow(int step); 72 void dfscol(int step); 73 int main() 74 { 75 int i; 76 77 while(scanf("%d",&n), n != EOF){ 78 for(i=1;i<=n;i++){ 79 scanf("%d%d%d%d",&rec[i].xl,&rec[i].yl,&rec[i].xr,&rec[i].yr); 80 } 81 82 memset(bkrow,0,sizeof(bkrow)); 83 memset(bkcol,0,sizeof(bkcol)); 84 flag1=flag2=0; 85 dfsrow(1); 86 dfscol(1); 87 88 if(flag1 && flag2){ 89 for(i=1;i<=n;i++){ 90 printf("%d %d\n",row[i],col[i]); 91 } 92 } 93 else 94 printf("IMPOSSIBLE\n"); 95 96 } 97 return 0; 98 } 99 100 void dfsrow(int step){ 101 if(step == n+1){ 102 flag1=1; 103 return; 104 } 105 106 int j; 107 for(j=rec[step].xl;j<=rec[step].xr;j++){ 108 if(bkrow[j] == 0){ 109 bkrow[j]=1; 110 row[step]=j; 111 dfsrow(step+1); 112 113 if(flag1) 114 return; 115 bkrow[j]=0; 116 } 117 } 118 } 119 120 void dfscol(int step){ 121 if(step == n+1){ 122 flag2=1; 123 return; 124 } 125 126 int j; 127 for(j=rec[step].yl;j<=rec[step].yr;j++){ 128 if(bkcol[j] == 0){ 129 bkcol[j]=1; 130 col[step]=j; 131 dfscol(step+1); 132 133 if(flag2) 134 return; 135 bkcol[j]=0; 136 } 137 } 138 }*/
解决这道题的过程还是一波三折的,搜索超时,贪心苦思冥想,最后还栽在了初始化函数memset上,可以看到平时大家使用初始化函数初始化数组时都这样写
1.memset(ans,-1,sizeof(int)*n);
2.memset(ans,-1,sizeof(ans));
意即将ans数组中的内存单元全部初始化为-1,其实只有第一种写法是正确的,这里错误的原因是VC函数传参过程中的指针降级,导致sizeof(a),返回的是一个something*指针类型大小的的字节数,如果是32位,就是4字节。(详见百度百科https://baike.baidu.com/item/memset/4747579?fr=aladdin#reference-[1]-982208-wrap)
而第二种用法可以出现在初始化结构体中。
欢迎交流,共同进步——