P1056 排座椅(贪心 排序 经典)

题的链接:P1056 排座椅

题解: 题目说输出最好的方案,说明方案不止一种,要输出每条切割线切得人最多的线,所以就是典型的贪心,先统计要切割线路的次数,再输出前K或L个元素(输出按照行列号从小到大输出)就行。

首先说明一个问题:给出的K和L是切割线的一部分,也就是所有切割线按从大到小排完序的行前K,列前L个;这样还不行,输出必须输出所切割行下标或列下标要从小到大输出,所以还得对要输出的前K或L个元素按照下标从小到大再排一次序,才能输出。
知识点:关于cmp函数参数中的&符号!

参考代码1.0: 结构体存储,自定义cmp调用sort排序

#include 
#include 
#include 
#include 
#include 
#include 
#define INF 0x3f3f3f3f
#define MAX 20010
using namespace std;

int M, N, K , L, D;
int x1, y1, x2, y2;

struct Node
{
    int w;//存储切割行或列出现的次数
    int v;//存储行标或列标
};

Node x[2010], y[2010];

bool cmp1(const Node &a, const Node &b)
{
    return a.w > b.w;
}

bool cmp2(const Node &a, const Node &b)
{
    return a.v < b.v;
}

int main()
{
    cin >> M >> N >> K >> L >> D;
    //初始化行或列下标
    for(int i = 1; i <= M; i++) x[i].v = i;
    for(int i = 1; i <= N; i++) y[i].v = i;
    while(D--)
    {
        cin >> x1 >> y1 >> x2 >> y2;
        if(x1 == x2)//列画线
        {
            int t = min(y1, y2);
            y[t].w++;//切割次数++
        }
        if(y1 == y2)//行画线
        {
            int t = min(x1, x2);
            x[t].w++;
        }
    }
    //按照切割数w从大到小排序
    sort(x, x + M, cmp1);
    sort(y, y + N, cmp1);
    //按照下标v从小到大排序前K,或前L个
    sort(x, x + K, cmp2);
    sort(y, y + L, cmp2);
    for(int i = 0; i < K; i++) cout << x[i].v <<" ";
    cout << endl;
    for(int i = 0; i < L; i++) cout << y[i].v <<" ";
    cout << endl;
    return 0;
}

参考代码2.0: 结构体存储,四次冒泡排序。切割次数从大到小,下标从小到大。

#include 
#include 
#include 
#include 
#include 
#include 
#define INF 0x3f3f3f3f
#define MAX 20010
using namespace std;

int M, N, K , L, D, pos;
int x1, y1, x2, y2;

struct Node
{
    int w, v;
};
Node x[2010], y[2010];

int main()
{
    cin >> M >> N >> K >> L >> D;
    while(D--)
    {
        cin >> x1 >> y1 >> x2 >> y2;
        if(x1 == x2)//列画线
        {
            int t = min(y1, y2);
            y[t].w++;//切割次数++
            y[t].v = t;
        }
        if(y1 == y2)//行画线
        {
            int t = min(x1, x2);
            x[t].w++;
            x[t].v = t;
        }
    }
    //四次冒泡排序
    for(int i = 0; i < M; i++)
        for(int j = M - 1; j >= i + 1; j--)
            if(x[j].w > x[j - 1].w) swap(x[j], x[j - 1]);

    for(int i = 0; i < N; i++)
        for(int j = N - 1; j >= i + 1; j--)
            if(y[j].w > y[j - 1].w) swap(y[j], y[j - 1]);

    for(int i = 0; i < K; i++)
        for(int j = K - 1; j >= i + 1; j--)
            if(x[j].v < x[j - 1].v) swap(x[j], x[j - 1]);

    for(int i = 0; i < L; i++)
        for(int j = L - 1; j >= i + 1; j--)
            if(y[j].v < y[j - 1].v) swap(y[j], y[j - 1]);

    for(int i = 0; i < K; i++) cout << x[i].v << " ";
    cout << endl;
    for(int i = 0; i < L; i++) cout << y[i].v << " ";
    cout << endl;
    return 0;
}


参考代码3.0: 没有用到排序和cmp,用数组存储切割下标的次数,再查找K次或L次最大切割数标记出来,将其存入新数组,for循环从小到大输出,(新数组除了K,L个数据,其他都是0元素)

#include 
#include 
#include 
#include 
#include 
#include 
#define INF 0x3f3f3f3f
#define MAX 20010
using namespace std;

int M, N, K , L, D, pos;
int x1, y1, x2, y2;
int x[2010], y[2010];
int xx[2010], yy[2010];

int main()
{
    cin >> M >> N >> K >> L >> D;
    while(D--)
    {
        cin >> x1 >> y1 >> x2 >> y2;
        if(x1 == x2)//列画线
        {
            int t = min(y1, y2);
            y[t]++;//切割次数++
        }
        if(y1 == y2)//行画线
        {
            int t = min(x1, x2);
            x[t]++;
        }
    }
    //(找K次最大值)每次将切割数最大的数的下标标记出来,存入数组xx
    for(int i = 0; i < K; i++)
    {
        int maxx = 0;
        for(int j = 1; j < M; j++)
        {
            if(maxx < x[j]) maxx = x[j], pos = j;
        }
        //存储后清零
        x[pos] = 0; xx[pos] = 1;
    }
    for(int i = 0; i < L; i++)
    {
        int maxx = 0;
        for(int j = 1; j < N; j++)
        {
            if(maxx < y[j]) maxx = y[j], pos = j;
        }
        y[pos] = 0; yy[pos] = 1;
    }
    //将切割数按照下标从小到大输出
    for(int i = 0; i < M; i++) if(xx[i]) cout << i <<" ";
    cout << endl;
    for(int i = 0; i < N; i++) if(yy[i]) cout << i <<" ";
    cout << endl;
    return 0;
}

你可能感兴趣的:(洛谷,算法与数据结构)