1. 程式人生 > >Voting-一道題來看set效率和queue效率

Voting-一道題來看set效率和queue效率

分析:策略是採取先deny第一個後於自己位置的對手,如果自己後面沒對手了就投剩下的對手中位置最靠前的,道理顯而易見,位置越前越有可能deny自己陣容的人,肯定要先“讓他說不了話”比較合理

一開始直接陣列模擬,while(1)遍歷陣列居然還能過前20多組資料,但這樣效率奇低,後來想用優先佇列(最小堆),每次把對面的根節點deny,但這樣不能保證是最優,例如樣test21的資料
7
RDRDDRD
這樣寫WA,原因是第三個D選擇deny的是第一個R而不是緊跟他的下一個R ,這顯然坑隊友,第四個D會被第三個R搞死(deny),

//WA 21 的版本
#include <iostream>
#include <cstdio> #include <queue> using namespace std; const int maxn = 200005; char s[maxn]; priority_queue<int,vector<int>,greater<int> > d,r; int a[maxn]={0},b[maxn]={0}; int main() { int n; scanf("%d",&n); scanf("%s",s); int i=0; for(i=0;i<n;++i){ if
(s[i]=='D'){ d.push(i); a[i]=1; } else { r.push(i); b[i]=1; } } i = -1; while(1){ if(++i>n)i=0; if(d.size()==0){ puts("R"); return 0; } if(r.size()==0){ puts
("D"); return 0; } if(a[i]){//D b[r.top()]=0; //printf("%d\n",r.top()); r.pop(); } else if(b[i]){//R a[d.top()]=0;; //printf("%d\n",d.top()); d.pop(); } } return 0; }

考慮到佇列沒查詢二分搜尋之類的,我糊里糊塗地用set做了,以為二分搜尋,查詢和刪除的複雜度是O(logN)時間會過,於是寫了下面這個版本
,仔細想一下其實肯定TLE啦,整個複雜度幾乎是O(n! *logN)

//TLE 12版本
#include <iostream>
#include <cstdio>
#include <cstring>
#include <set>
using namespace std;
const int maxn = 200050;
char s[maxn];
int d[maxn]={0},r[maxn]={0};
set<int> de,re;
int main()
{
    int n;
    scanf("%d",&n);
    scanf("%s",s+1);

    int i = 0;
    for(i=1;i<=n;++i){
        if(s[i]=='D'){
            d[i]=1;
            de.insert(i);
        }else{
            r[i]=1;
            re.insert(i);
        }
    }

    i = 0;
    set<int>::iterator p;
    while(1){
        if(++i > n)i=1;
        if(re.size()==0){
            puts("D");
            return 0;
        }
        if(de.size()==0){
            puts("R");
            return 0;
        }

        if(r[i]){

            p = lower_bound(de.begin(),de.end(),i);
            if(p != de.end()){
                d[ *p ] = 0;
                de.erase(p);
            }else{
                if(de.size()>0){
                    d[ *de.begin() ] = 0;
                    de.erase(de.begin());
                }
            }   
        }else if(d[i]){

            p = lower_bound(re.begin(),re.end(),i);
            if(p != re.end()){
                r[ *p ] = 0;
                re.erase(p);
            }else{
                if(re.size()>0){
                    r[ *re.begin() ] = 0;
                    re.erase(re.begin());
                }
            }   
        }   
    }

    return 0;
}

其實這題想明白一點就能做了,兩隊分別用兩個佇列存vote的時間,顯然隊首小的先投,肯定是投死對面隊首的比較有利,然後自己加入回隊尾,注意加入隊尾時時間要加n輪,代表自己投死對面隊首後隔n輪才能再投(如果還合法的話),那會不會對手把自己投死但自己還在佇列中這種情況出現呢?顯然不會,因為是理智的,所以肯定都會先投對面隊首的,如果你出現在隊首說明對手對你的隊伍的選擇優先的只有你了,即你出現就是合理的
想明白就很好寫了,時間31ms

#include <iostream>
#include <cstdio>
#include <queue>
using namespace std;
const int maxn = 200050;
char s[maxn];
queue<int> de,re;
int main()
{
    int n;
    scanf("%d",&n);
    scanf("%s",s+1);

    for(int i=1;i<=n;++i){
        if(s[i]=='D')
            de.push(i);
        else
            re.push(i);
    }   
    while(1){       
        if(re.empty()){
            puts("D");
            return 0;
        }
        if(de.empty()){
            puts("R");
            return 0;
        }
        if(de.front() < re.front()){//D votes
            re.pop();
            de.push(de.front()+n);
            de.pop();
        }else{//R votes
            de.pop();
            re.push(re.front()+n);
            re.pop();
        }
    }
    return 0;
}