[COCI2017-2018 Contest4] Izbori

前言

又是没有写博客的半个月

没碰 C S D N CSDN CSDN的这些天过得可真充实~~

水一篇水题祭手~

题目

题目描述

(让人看不下去的英文题目) 请不想挑战英语水平的童鞋们直接往下翻

In a land with developed democracy far, far away, presidential elections for the football association are taking place. This land consists of N counties, and each county has its own football association. There are M presidential candidates labeled with 1, 2, … M. Each of the football associations will select exactly one candidate to cast their vote for. The winner of the election is the candidate with the most votes. If multiple candidates get the most amount of votes, the winner is the one with the smallest label.

During the election campaign, candidates visited the counties and tried to gain their sympathies. After having met all the candidates, each county’s football association determined the order in which they would cast their vote for each candidate.

For example, let’s assume that there are four candidates in the election and that one county’s order is 2, 1, 4, 3. This means that, unless they revoke their candidacy, the candidate with label 2 will get the county’s vote. If candidate 2 revokes their candidacy, and candidate 1 is still in the race, then they will get the vote, and so on.

Zdravko is a passionate football fan, and also a close friend of candidate with label K. He wants to know which candidate will win if neither of the candidates revokes their candidacy.

He also wants to know what is the minimal number of candidates he must persuade to revoke their candidacy in order for his friend, candidate K, to become the president of the football association.

Zdravko is currently dealing with other problems, so he is hoping that you will answer these questions.

输入格式

The first line of input contains the numbers N (1 ≤ N ≤ 100), M (1 ≤ M ≤ 15) and K (1 ≤ K ≤ M) from the task.

Each of the following N lines contains the orders given by the counties’ football associations, i.e. a permutation of the first M natural numbers.

输出格式

You must output the answers to the questions from the task, each in its own line.

输入输出样例

样例输入1

3 4 1
3 4 1 2
4 2 3 1
3 4 2 1

样例输出1

3
3

样例输入2

4 1 1
1
1
1
1

样例输出2

1
0

样例输入3

4 4 4
2 3 1 4
2 3 1 4
1 3 2 4
4 3 2 1

样例输出3

2
3

中文版

题目描述
n n n个县会对 m m m个候选人投票,他们会按照一定顺序投票,除非前一个候选人退出选举,否则不会投下一个候选人的票,现在问你哪一个候选人能够获胜?请问至少让多少候选人退出,才能保证候选人 k k k获胜。
如果说选票一样,那么编号小的就获胜
输入
第一行输入三个数字 N N N 1 ≤ N ≤ 100 1≤N≤100 1N100), M M M 1 ≤ M ≤ 15 1≤M≤15 1M15), K K K 1 ≤ K ≤ M 1≤K≤M 1KM

接下来 N N N行,每行输入 M M M个数字,表示第i个县投票的顺序。
输出
第一行输出如果所有候选人都不退出,最终的获胜者是谁。

第二行输出如果要使 K K K胜出,最少需要劝退多少名候选人。

解析

送分问

那么刚拿到这个题,就能够看出来,第一个问题实在简单,只需要在读入的时候处理一下就ok了。

参考代码

for (int i = 1; i <= n; i++){
        for (int j = 1; j <= m; j++)
            re (ord[i][j]);
        vod[ord[i][1]] ++;
        if (vod[ord[i][1]] > maxn || (vod[ord[i][1]] == maxn && ord[i][1] < pos))
            maxn = vod[ord[i][1]], pos = ord[i][1];
    }

在这里注意一下上面的标黑的句子,如果说票数是一样的话,那么编号小的会获胜。

送命问

关键就是第二个问题怎么搞?毕竟是 t 2 t2 t2,也不会特别难,因此我们就往简单的方向考虑。

因此我们思考一下就能够想出来一个方法,类似 I D D F S IDDFS IDDFS,只需要枚举一下最少会让多少个人退出,然后再 c h e c k check check一下差不多就行了。

c h e c k check check也不会太难,利用贪心思想,每次将当时的票数最高的人退出,然后统计新的票数,直到最高的票数是 k k k为止。

参考代码

void check (int id){
    vod[id] = 0; vis[id] = 1;
    for (int i = 1; i <= n; i++){
        ord[i][0] = 1;
        while (vod[ord[i][ord[i][0]]] == 0 && vis[ord[i][ord[i][0]]] && ord[i][0] < m)
            ord[i][0] ++;
        vod[ord[i][ord[i][0]]] ++;
        if (vod[ord[i][ord[i][0]]] > maxn)
            maxn = vod[ord[i][ord[i][0]]], pos = ord[i][ord[i][0]];
    }
}
int main (){
	for (int i = 1; i <= m; i++){
        maxn = 0;
        check (pos);
        if (pos == k){
            pr (i); putchar (10);
            return 0;
        }
    }
}

当然在这我加了一个小技巧,就是直接从上一次退出的那个人开始枚举,如果他不是要退出或已退出的人,那么就将他的选票加上。

这样的话就不用从1枚举到m来加上选票(其实意义不大,毕竟m最大就只是15,而且我也并不能保证这样就是对的 手动滑稽

这片题解到此结束,大家可以离开了

真正的题解

哈哈哈哈哈哈嗝,不知道有没有人真的信了上面的话,直接开码然后交了啊?

不知道为什么,所有的贪心思路所形成的代码分数都 ≤ 20 \leq20 20分。。。

好吧接下来讲真正的题解:

其实思路确实很简单,暴力枚举所有的人,看到底需不需要让他退出、、、

其实说白了就是爆搜,而且时间十分充裕,完全可以跑完

#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <set>
#include <map>
#include <string>
#include <queue>
#include <stack>
#include <cstring>
#include <iostream>
using namespace std;
#define reg register
#define LL long long
#define INF 0x3f3f3f3f

template<typename T>
void re (T &x){
    x = 0;
    int f = 1;
    char c = getchar ();
    while (c < '0' || c > '9'){
        if (c == '-') f = -1;
        c = getchar ();
    }
    while (c >= '0' && c <= '9'){
        x = (x << 1) + (x << 3) + c - 48;
        c = getchar ();
    }
    x *= f;
}

template<typename T>
void pr (T x){
    if (x < 0){
        putchar ('-');
        x = ~x + 1;
    }
    if (x / 10) pr (x / 10);
    putchar (x % 10 + 48);
}

int n, m, k, ord[105][15], ans = INF, maxn, pos;
int vod[20];
bool vis[20];

void dfs (int indx, int sum){
    maxn = pos = 0;
    for (int i = 1; i <= m; i++)
        vod[i] = 0;
    for (int i = 1; i <= n; i++){
        int j = 1;
        while (vis[ord[i][j]] && j < m)
            j ++;
        vod[ord[i][j]] ++;
        if (vod[ord[i][j]] > maxn || (vod[ord[i][j]] == maxn && ord[i][j] < pos))
            maxn = vod[ord[i][j]], pos = ord[i][j];
    }
    if (pos == k){
        ans = min (ans, sum);
        return ;
    }
    for (int i = indx; i <= m; i++){
        if (!vis[i] && i != k){
            vis[i] = 1;
            dfs (i, sum + 1);
            vis[i] = 0;
        }
    }
}

int main (){
    //freopen ("izbori.in", "r", stdin);
    //freopen ("izbori.out", "w", stdout);
    re (n); re (m); re (k);
    for (int i = 1; i <= n; i++){
        for (int j = 1; j <= m; j++)
            re (ord[i][j]);
        vod[ord[i][1]] ++;
        if (vod[ord[i][1]] > maxn || (vod[ord[i][1]] == maxn && ord[i][1] < pos))
            maxn = vod[ord[i][1]], pos = ord[i][1];
    }
    pr (pos); putchar (10);
    if (pos == k){
        pr (0); putchar (10);
        return 0;
    }
    dfs (1, 0);
    pr (ans);
    putchar (10);
    return 0;
}
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值