1. 程式人生 > >洛谷 P2831 [NOIP2016]憤怒的小鳥

洛谷 P2831 [NOIP2016]憤怒的小鳥

傳送

一些廢話

題太難了,於是這次不寫解析,具體思路可以看這位聚聚的部落格:

聚聚的部落格

這裡說對搜尋題的一點看法。

首先是實數域上的搜尋。對於本題而言,顯然拋物線的每個引數都是實數,斷絕了一般列舉的可能。對於這類情況,常用的轉化是:

列舉所有的情況 -> 列舉可能的情況

對於本題,具體而言,顯然絕大部分拋物線都是無用的,而兩隻豬的座標可以確定一條過原點的拋物線(當然,別忘了排除不合法的情況),所以我們列舉所有這樣的豬對即可。

與本題類似地,有 \(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;
}