1021 Safe Fruit (35 分)(C++)

PAT頂級題解目錄

There are a lot of tips telling us that some fruits must not be eaten with some other fruits, or we might get ourselves in serious trouble. For example, bananas can not be eaten with cantaloupe (哈密瓜), otherwise it will lead to kidney deficiency (腎虛).

Now you are given a long list of such tips, and a big basket of fruits. You are supposed to pick up those fruits so that it is safe to eat any of them.

Input Specification:

Each input file contains one test case. For each case, the first line gives two positive integers: N, the number of tips, and M, the number of fruits in the basket. both numbers are no more than 100.

Then two blocks follow. The first block contains N pairs of fruits which must not be eaten together, each pair occupies a line and there is no duplicated tips; and the second one contains M fruits together with their prices, again each pair in a line. To make it simple, each fruit is represented by a 3-digit ID number. A price is a positive integer which is no more than 1000. All the numbers in a line are separated by spaces.

Output Specification:

For each case, first print in a line the maximum number of safe fruits. Then in the next line list all the safe fruits, in increasing order of their ID's. The ID's must be separated by exactly one space, and there must be no extra space at the end of the line. Finally in the third line print the total price of the above fruits. Since there may be many different solutions, you are supposed to output the one with a maximum number of safe fruits. In case there is a tie, output the one with the lowest total price. It is guaranteed that such a solution is unique.

Sample Input:

16 20
001 002
003 004
004 005
005 006
006 007
007 008
008 003
009 010
009 011
009 012
009 013
010 014
011 015
012 016
012 017
013 018
020 99
019 99
018 4
017 2
016 3
015 6
014 5
013 1
012 1
011 1
010 1
009 10
008 1
007 2
006 5
005 3
004 4
003 6
002 1
001 2

Sample Output:

12
002 004 006 008 009 014 015 016 017 018 019 020
239

題目大意:有編號爲xxx的水果,給出一些tips,每個tips表示哪兩種水果不可以同時喫,同時給出每個水果的價格。現在要求怎樣纔可以喫到嘴多的水果,如果有兩種數量相同的喫法,選擇價格低的那種。

注意:最後一個測試點給出的n個tips中可能有某種水果並沒有給出price,也就是無效tips,要去除掉。

DFS+dp(Bron-Kerbosch算法

推薦:最大獨立集算法

解題思路:最大獨立集算法(模板題),最大獨立集算法和最大團算法是一致的,求某個圖的最大獨立集=求該圖的補圖的最大團。本題其實給出的邊是補圖中邊。

代碼:

#include <bits/stdc++.h>
using namespace std;
const int SIZE = 1005;
int mat[SIZE][SIZE]={0}, dp[SIZE], Stack[SIZE][SIZE];
vector<int>idToIndex(SIZE, 105), indexToId(SIZE), tips_id[SIZE];
vector<int>temppath, path;
int price[SIZE], n, m, a, b, tempans = 0, ans;
int mx;
void dfs(int N, int num, int step){
    if(num == 0){
        if(step > mx || (step == mx && tempans < ans)){
            ans = tempans;
            mx = step;
            path = temppath;
        }
        return;
    }
    for(int i = 0; i < num; ++ i){
        int k = Stack[step][i];
        if(step+N-k < mx)
            return;
        if(step+dp[k] < mx)
            return;
        int cnt = 0;
        for(int j = i + 1; j < num; ++ j)
            if(!mat[k][Stack[step][j]])
                Stack[step+1][cnt++] = Stack[step][j];
        temppath.push_back(k);
        tempans += price[k];
        dfs(N, cnt, step+1);
        tempans -= price[k];
        temppath.pop_back();
    }
}
void run(int N){
    mx = 0;
    for(int i = N-1; i >= 0; --i){
        int sz = 0;
        for(int j = i+1; j < N; ++ j)
            if(!mat[i][j])
                Stack[1][sz++] = j;
        temppath.push_back(i);
        tempans += price[i];
        dfs(N, sz, 1);
        tempans -= price[i];
        temppath.pop_back();
        dp[i] = mx;
    }
}
int main(){
    scanf("%d %d", &n, &m);
    for(int i = 0; i < n; ++ i){
        scanf("%d %d", &a, &b);
        tips_id[a].push_back(b);
    }
    for(int i = 0; i < m; ++ i){
        scanf("%d %d", &a, &price[i]);
        indexToId[i] = a;
        idToIndex[a] = i;
    }
    for(int i = 0; i < m; ++ i){
    	mat[i][i] = 1;
        for(int j : tips_id[indexToId[i]])
            if(idToIndex[j] < m)
                mat[i][idToIndex[j]] = mat[idToIndex[j]][i] = 1;
    }
    run(m); 
    for(int i = 0; i < path.size(); ++ i)
        path[i] = indexToId[path[i]];
    sort(path.begin(), path.end());
    printf("%d\n%03d", mx, path[0]);
    for(int i = 1; i < path.size(); ++ i)
        printf(" %03d", path[i]);
    printf("\n%d", ans);
}

PS:另附上一份沒用dp剪枝的DFS,倒數第二個測試點會超時~~

解題思路:DFS剪枝

這裏的visit數組跟常見的不太一樣,不是單純用布爾值記錄是否能訪問,直接記錄數字,有多少次影響了該id的水果。只有當visit處的數字爲0,纔可以加到tempans中。

目前倒數第二個測試點超時,暫時也沒有想到其他剪枝方法(除了dp那個),有想法的歡迎評論~~

代碼:

#include <bits/stdc++.h>
using namespace std;
int indexToId[1005], price[1005];
int n, m, a, b, sum, tempsum = 0;
std::vector<int> tips_id[1005], visit(1005, 0), ans, tempans;
void dfs(int index){
    int temp = indexToId[index];
    while(index < m - 1 && visit[temp] != 0)
        temp = indexToId[++index];
    if(index == m || visit[temp] != 0){
        if(ans.size() < tempans.size() || (ans.size() == tempans.size() && sum > tempsum)){
            ans = tempans;
            sum = tempsum;
        }
        return;
    }
    if(ans.size() > m - index + tempans.size())
        return;
    visit[temp] += 1;
    tempsum += price[temp];
    tempans.push_back(temp);
    for(int tip : tips_id[temp])
        visit[tip] += 1;
    int x = index;
    dfs(index + 1);
    for(int tip : tips_id[temp])
        visit[tip] -= 1;
    tempans.pop_back();
    tempsum -= price[temp];
    visit[temp] -= 1;
    dfs(index + 1);
}
int main(){
    scanf("%d %d", &n, &m);
    for(int i = 0; i < n; ++ i){
        scanf("%d %d", &a, &b);
        tips_id[a].push_back(b);
        tips_id[b].push_back(a);
    }
    for(int i = 0; i < m; ++ i){
        scanf("%d %d", &a, &b);
        price[a] = b;
        indexToId[i] = a;
    }
    dfs(0); 
    sort(ans.begin(), ans.end());
    printf("%d\n%03d", ans.size(), ans[0]);
    for(int i = 1; i < ans.size(); ++ i)
        printf(" %03d", ans[i]);
    printf("\n%d", sum);
} 

另外最大團的模板

#include <bits/stdc++.h>
using namespace std;
const int SIZE = 105;
/*
dp[i]表示從i到n-1中的最大團的節點數。
枚舉每一個節點,看這個節點與哪些編號大於它的節點相連,記錄這些節點,然後遞歸地處理這些節點。。。
怎樣去做剪枝那?
假如已經到x個節點在“團”裏的狀態,處理到了第k個節點,判斷
x+n-k <= mx
和
x+dp[k] <= mx
*/
int mat[SIZE][SIZE];  /*圖矩陣*/
int dp[SIZE];
int mx;
int stack[SIZE][SIZE];
void dfs(int N,int num,int step){
 
    if(num==0){
        if(step > mx){
            mx=step;
        }
        return ;
    }
 
    for(int i=0;i<num;i++){
        int k = stack[step][i];
        if(step+N-k<=mx) return ;
        if(step+dp[k]<=mx) return ;
        int cnt = 0;
        for(int j=i+1;j<num;j++)
            if(mat[k][stack[step][j]]){
                 stack[step+1][cnt++]=stack[step][j];
            }
        dfs(N,cnt,step+1);
    }
}
 
void run(int N){
 
    mx =0;
    for(int i=N-1;i>=0;i--){
        int sz =0;
        for(int j=i+1;j<N;j++)
            if(mat[i][j]) stack[1][sz++]=j;
        dfs(N,sz,1);
        dp[i]=mx;
    }
}

 

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