Educational Codeforces Round 42 (Rated for Div. 2) D. Merge Equals(problems 962)

D. Merge Equals

題目傳送門
題目大意就是給一個數組,每次合併左邊最小的兩個數(左邊的合併到右邊去),一直合併到沒法合併爲止java


前幾天與之前的ACM的隊友們聊天,忽然感觸良深.因而一塊兒跑去CF作題了(一年沒摸過了).
在作完這題以後我發現個人想法效率極低,因而我去問隊友們都是怎麼實現的,結果發現根據每一個人的特性,各自的作法都有些不一樣. 其中,方案四算是咱們認爲的最優解了.
另外能夠根據每一個人的實現思考下我是怎麼把簡單的問題複雜化了....c++


一 最low的實現(java)

這也是個人實現,效率真的低並且浪費資源,換一年前的本身根本不敢想象能寫出這樣的代碼,看來真的是過久不思考了,麻木了.大家當笑話看吧 數組

個人提交結果
簡單圖解
個人作法爲每個[數字]都建了一個優先隊列,裏面存儲了數字的下標.而後從小到大合併,並放到[數字*2]裏. 具體反面教材實現以下:

public static void main(String[] args) {

        Scanner scan = new Scanner(System.in);
        int n = scan.nextInt();
        Long[] input = new Long[n];
        TreeMap<Long, PriorityQueue<Integer>> positionMap = new TreeMap<>();
        for (Integer i = 0; i < n; i++) {
            input[i] = scan.nextLong();
            PriorityQueue<Integer> position = positionMap.computeIfAbsent(input[i], aLong -> new PriorityQueue<>());
            position.add(i);
        }

        TreeMap<Integer, Long> sortedMap = new TreeMap<>();
        //每次從新獲取迭代器.
        for (Iterator<Long> iter = positionMap.keySet().iterator(); iter.hasNext(); iter = positionMap.keySet().iterator()) {
            //數字
            Long key = iter.next();
            //將相同數據進行合併存入Map,新key爲(key+key)
            PriorityQueue<Integer> position = positionMap.get(key);
            //若是大於1,進行合併操做
            if (position.size() > 1) {
                PriorityQueue<Integer> upgradePos = positionMap.get(key + key);
                if (upgradePos == null) {
                    upgradePos = new PriorityQueue<>();
                    positionMap.put(key + key, upgradePos);
                }
                //是否升級到key+key
                boolean upFlag = false;
                //是否須要保留最後一個數(奇數保留)
                boolean remainFlag = (position.size() & 1) == 1;
                while (!position.isEmpty()) {
                    //數字位置
                    Integer pos = position.poll();
                    if (upFlag) {
                        upgradePos.add(pos);
                    }
                    if (remainFlag && position.size() == 1) {
                        break;
                    }
                    upFlag = !upFlag;
                }
            }
            //當前key處理完,存入結果集,將key從原map中移除
            if (!position.isEmpty()) {
                sortedMap.put(position.poll(), key);
            }
            positionMap.remove(key);
        }
        //根據最終下標輸出num
        System.out.println(sortedMap.size());
        Iterator<Integer> iterator = sortedMap.keySet().iterator();
        while (iterator.hasNext()) {
            System.out.print(sortedMap.get(iterator.next()));
            if (iterator.hasNext()) {
                System.out.print(" ");
            }
        }
        System.out.println();
    }
複製代碼

二 上述方案的優化版本(C++)(XTTT)

優化事後

        上面的方案太智障了,以後被學弟xttt吐槽了,還順便幫我優化了下.
        優化思路大體就是隻使用一個優先隊列,代碼簡單,實現容易.
我以前是爲每一個數字開了一個優先隊列,而XTTT直接使用一個隊列,而後往隊列裏存儲[數字,下標]的數據.而後一個個出棧作合併操做,合併完入棧,沒法合併了(開設一個數組記錄數字數量)就存入結果集

#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1

const int maxn=150010;
const long long INF=(1ll<<60)-1;
typedef long long ll;
typedef pair<ll,int> pli;
map<ll,int> cnt;
//用來存儲數字的數量
map<int,ll> ans;

priority_queue<pli,vector<pli>,greater<pli> >Q;
int main(){
    //freopen("D://input.txt","r",stdin);

    int n;
    scanf("%d",&n);
    //往隊列內存入 {數字,下標} 的數據
    for(int i=1;i<=n;i++){
        ll a;scanf("%I64d",&a);
        Q.push(make_pair(a,i));
        cnt[a]++;
    }
    //遍歷隊列
    while(!Q.empty()){
        pli it=Q.top();Q.pop();
        ll small=it.first;
        int p=it.second;
        //若是當前數字剩餘數量大於1
        if(cnt[small]>1){
            //與下一個當前數字進行合併後入隊列
            cnt[small]-=2;
            cnt[small*2]++;
            pli it2=Q.top();Q.pop();
            it2.first*=2;
            Q.push(it2);
        }
        else{
            //若只剩一個了,則存入結果集
            cnt[small]--;
            ans[p]=small;
        }
    }

    printf("%d\n",ans.size());
    for(map<int,ll>::iterator it=ans.begin();it!=ans.end();it++){
        printf("%I64d ",it->second);
    }
    return 0;
}
複製代碼

三 因式分解方式實現(c++)(dadi)

dadi因式分解
        這是我當初的隊友dadi(大地)的實現方案,之前打比賽的時候就一直以爲他數學思惟能力很強,因此我十分好奇他會以什麼樣的思路來解這道題,果不其然,他一上來就看出了這題數據的本質.
        他將全部數字分解成 k*(2^n)的形式,能夠較快得出最後留下的數字是啥,可是最終下標的記錄方式與咱們有點相似,具體實現我也沒看,直接上代碼吧.

struct P{
	int ss,lv,tos;
}p[150005];
struct Q{
	int tos;
	long long lv;
};
Q s[150005];
bool comp (P a, P b) {
	if (a.ss != b.ss)
		return a.ss < b.ss;
	return a.tos < b.tos;
}
int main() {
	int n;
	scanf("%d",&n);
	
	int a;
	for (int i = 0; i < n; i++){
		scanf("%d",&a);
		int j = 1;
		while(a % 2 == 0){
			j *= 2;
			a >>= 1;
		}
		p[i].ss = a;
		p[i].lv = j;
		p[i].tos = i;
	}
	sort(p, p + n, comp);
	long long aa[150005];
	memset(aa, 0, sizeof(aa));
	int e = 0;
	for (int i = 0; i < n;)
	{
		long long sum = 0;
		int k = 0;
		int j;
		for (j = i ; j < n; j++ )
		{
			if (p[j].ss != p[i].ss)
				break;
			s[k].lv = p[j].lv;
			s[k++].tos = p[j].tos;
			sum += p[j].lv;
		}
		long long ans = p[i].ss;
		i = j;
		int b[50];
		memset(b, 0, sizeof(b));
		long long temp = sum;
		int bb;
		for (j = 0 ; temp; j++)
		{
			if (temp % 2){
				b[j] = 1;
			}
			temp /= 2;
		}
		bb = j;
		temp = 1;
		for (j = 0; j < bb; j++){
			int c = 0;
			bool d = true;
			for (int l = k - 1; l >= 0; l--){
				if (s[l].lv == temp)
					c++;
				else
					continue;
				if (d && b[j]){
					//printf("&%d %d %lld %lld\n", j, s[l].tos, temp, ans);
					aa[s[l].tos] = ans * temp;
					e++;
					d = false;
				}
				if ((c + b[j]) % 2 == 1)
					s[l].lv *= 2;
				else
					s[l].lv = 0;
			}
			temp *= 2;
		}
		
	}
	printf("%d\n", e);
	for (int i = 0, j = 0; i < n; i++){
		if (aa[i]){
			if (j)
				printf(" ");
			printf("%lld",aa[i]);
			j++;
		}
	}
	printf("\n");
}
複製代碼

四 一致認爲的最優解(c++)(小籠包)

        小籠包學弟摸上這題沒多久就作完了,並且實現上也是最簡單易懂的.咱們一致認爲這種方法算是標解,算是方案一,二的最終進化版了.
        實際上這道題的邏輯順序就是從左到右執行,掃一次就能完成,前面的方法都有點想太多了.小籠包的作法是輸入一個數就存入map,若已存在,則將這個數map中的值置爲null,而後把(key:這個數*2,value:下標)存入這個map中(每次存入都得判斷是否已存在).
優化

結果

typedef long long lint;

const int MAXN = 2E5;

map<lint,int> mp;
vector<lint> ans;
lint n,cnt;
bool can[MAXN];

int main() {
    while(scanf("%d",&n)!=EOF){
        mp.clear(); ans.clear(); cnt = 0; memset(can,true,sizeof(can));
        for(int i = 1; i <= n; ++i){
            lint num; scanf("%I64d",&num); ans.push_back(num);
            while(mp[num] != NULL){
                ans[i - 1] *= 2; can[mp[num] - 1] = false; mp[num] = NULL; num *= 2; ++cnt;
            }
            mp[num] = i;
        }
        printf("%d\n",n - cnt);
        for(int i = 0; i < ans.size(); ++i){
            if(can[i]){
                printf("%I64d ",ans[i]);
            }
        }
    }
    return 0;
}

複製代碼
相關文章
相關標籤/搜索