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;
    }
}

 

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