Vijos P1228 拯救世界-星際大戰

P1228拯救世界-星際大戰

描述

外星人逐漸逼近,爲了保護地球,現在決定直接在外空進行戰鬥。

現在我們有N個導彈。需要在最短的時間內,用這N個導彈摧毀敵方n個目標(1個導彈只能摧毀1個目標)。N個導彈和目標的位置不一定相同,但是給每個導彈確定目標是一件很麻煩的事情。請你編程幫助給每個導彈確定目標,使每個導彈到其目標的距離之和最小。

格式

輸入格式

第一行輸入N(N<=20)
接下來N行每行包含一個座標(x,y),表示一個導彈,-10000<x,y<10000,且x,y爲整數。

接下來N行每行包含一個座標(x,y),表示一個目標,-10000<x,y<10000,且x,y爲整數。

輸出格式

每個導彈到其目標距離之和的最小值。結果保留3位小數。

樣例1

樣例輸入1[複製]

1
10 1
6 -1

樣例輸出1[複製]

4.472


一開始是這樣想的(錯誤):我先預處理出來任意兩個點之間的距離,那麼就是一個n*n的矩陣,(n <= 20)
那麼問題轉化爲在這個矩陣上面選不同行不同列的n個點,且這n個點的和最小
那麼就用區間DP來寫
設dp[i][j][k]設以點i,j爲起點,邊長爲k的方形中取k個點所得的最小值,那麼我以爲任意一個正方形都可以轉化爲兩個成對角的小方形拼起來的,(滿足不同行不同列)(如圖左)
然而這個是錯誤的觀點,反例見右圖。。。。
可憐我寫了半天,居然還過了5個測試點
比賽時候想法如果錯了才坑
錯誤代碼見我的代碼片


正解:設DP[i][j]爲用到第i個導彈時候,目標狀態爲j時的最優解(目標狀態用二進制編碼,1 代表打過,0代表沒打過)
這樣的話就很簡單了,,
詳情見代碼

據說可以用隨機化搜索搞
也可以用二分圖




評測結果

編譯成功

測試數據 #0: Accepted, time = 0 ms, mem = 209880 KiB, score = 10

測試數據 #1: Accepted, time = 0 ms, mem = 209880 KiB, score = 10

測試數據 #2: Accepted, time = 0 ms, mem = 209880 KiB, score = 10

測試數據 #3: Accepted, time = 0 ms, mem = 209880 KiB, score = 10

測試數據 #4: Accepted, time = 0 ms, mem = 209880 KiB, score = 10

測試數據 #5: Accepted, time = 0 ms, mem = 209880 KiB, score = 10

測試數據 #6: Accepted, time = 0 ms, mem = 209880 KiB, score = 10

測試數據 #7: Accepted, time = 0 ms, mem = 209880 KiB, score = 10

測試數據 #8: Accepted, time = 0 ms, mem = 209880 KiB, score = 10

測試數據 #9: Accepted, time = 0 ms, mem = 209880 KiB, score = 10

Accepted, time = 0 ms, mem = 209880 KiB, score = 100

代碼:
#include 
#define maxn (1 << 20) + 100
#define inf 0x3f3f3f3f
using namespace std;

struct Node {
    double x;
    double y;
} a[25],b[25];

double dp[25][maxn];
int cnt[maxn];

int Count1(int n) {
    int cnt = 0;
    while(n != 0) {
        if(n % 2 == 1)  cnt ++;
        n /= 2;
    }
    return cnt;
}

double Dis(Node p,Node q) {
    return sqrt((q.x - p.x)  * (q.x - p.x) + (q.y - p.y) * (q.y - p.y));
}

int main() {
//    freopen("in.txt","r",stdin);
//    freopen("out.txt","w",stdout);
    int n;
    scanf("%d",&n);
    for(int i = 1; i <= (1 << n) - 1; i ++)   cnt[i] = Count1(i);
    for(int i = 1; i <= n; i ++)  scanf("%lf%lf",&a[i].x,&a[i].y);
    for(int i = 1; i <= n; i ++)  scanf("%lf%lf",&b[i].x,&b[i].y);
    for(int i = 0; i < n; i ++)  dp[1][1 << i] = Dis(a[1],b[i + 1]);
    for(int i = 2; i <= n; i ++) {
        for(int j = 1; j <= (1 << n) - 1; j ++) {
            dp[i][j] = inf;
            if(cnt[j] == i) {
                for(int k = 0; k < n; k ++) {
                    if(((1 << k) & j) == 0) continue;
                    int last = j - (1 << k);
                    dp[i][j] = min(dp[i][j],dp[i - 1][last] + Dis(a[i],b[k + 1]));
                }
            }
        }
    }
    printf("%.3lf\n",dp[n][(1 << n) - 1]);
    return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章