洛谷 P2831 [NOIP2016]憤怒的小鳥
阿新 • • 發佈:2018-10-31
一些廢話
題太難了,於是這次不寫解析,具體思路可以看這位聚聚的部落格:
這裡說對搜尋題的一點看法。
首先是實數域上的搜尋。對於本題而言,顯然拋物線的每個引數都是實數,斷絕了一般列舉的可能。對於這類情況,常用的轉化是:
列舉所有的情況 -> 列舉可能的情況
對於本題,具體而言,顯然絕大部分拋物線都是無用的,而兩隻豬的座標可以確定一條過原點的拋物線(當然,別忘了排除不合法的情況),所以我們列舉所有這樣的豬對即可。
與本題類似地,有 \(uva303\) ,如果有興趣可以去看一眼。
其次,每條拋物線可能擊殺多隻豬,但每次都去判斷是否擊殺某隻豬過於奢侈,於是我們對每條拋物線就算一次,把能擊殺的豬用二進位制數存起來。這也是一個常用的優化策略避免贅餘運算
以上。
參考程式
#include<iostream> #include<vector> #include<cmath> #include<cstring> using namespace std; int n,m,t,bin[20][20],mem[1<<18]; const double eps = 1e-7; const int inf = 0x3f3f3f3f; struct pig{ double x,y; }p[20]; inline double calca(pig i,pig j){ return (j.x * i.y - i.x * j.y) / (i.x * j.x * (i.x - j.x)); } inline double calcb(pig i,pig j){ return (i.x * i.x * j.y - j.x * j.x * i.y) / (i.x * j.x * (i.x - j.x)); } void calc(double a,double b){ vector<int> kill; int state = 0; for(int i = 0;i < n;i++) if(fabs(a * p[i].x * p[i].x + b * p[i].x - p[i].y) < eps){ state |= (1<<i); kill.push_back(i); } for(int i = 0;i < kill.size();i++) for(int j = i + 1;j < kill.size();j++) bin[kill[i]][kill[j]] = bin[kill[j]][kill[i]] = state; } int dfs(int state){ if(mem[state] != -1) return mem[state]; mem[state] = inf; for(int i = 0;i < n;i++){ if(state & (1<<i)) continue; bool flag = 0; for(int j = 0;j < n;j++) if((state & (1<<j)) == 0 && p[i].x != p[j].x && calca(p[i],p[j]) < 0.0){ flag = 1; if(bin[i][j] == -1) calc(calca(p[i],p[j]),calcb(p[i],p[j])); mem[state] = min(mem[state],dfs(state | bin[i][j]) + 1); } if(!flag) return mem[state] = dfs(state | (1<<i)) + 1; } return mem[state]; } int main(){ cin >> t; while(t--){ cin >> n >> m; for(int i = 0;i < n;i++) cin >> p[i].x >> p[i].y; memset(bin,-1,sizeof(bin)); memset(mem,-1,sizeof(mem)); mem[(1<<n)-1] = 0; cout << dfs(0) << endl; } return 0; }