• [算进] 巴士


    Problem

    ACwing 题目地址

    Preface

    我是真的想 bb 两句了,,,这个题目超级简单,但是因为 看错题目x2 导致影响我一天的心情。。。

    当然我要把好的题解和心情带给你们呀 qwq!

    Solution

    首先注意:这题的公交线路是一个严格在 (0-59) 范围内的等差数列,比如 (3,6,9,12) 就是一个不合法的公交路线,而 (3,6,9,...,57) 就是一个合法的公交路线

    完全读懂题目后,我们可以很快想到解题思路————

    • (1.) 预处理出来所有合法的公交路线,每个公交路线是一个等差数列,用二元组 ((x,d)) 来表示,(x) 是首项,(d) 是公差,说明这个路线的公交车到站时刻分布在 (x+d,x+2d,...,x+kd)(k) 是满足 (x+kd<60) 的最大非负整数)这些时刻。

    预处理这里有一个 细节: 注意 (3,6,9,...,57)(6,9,...,57),后者甚至不是一个合法的公交路线。我们要避免枚举这种情况,就要保证 (x-d<0 => d>x) (这样就保证了 (x-d) 不在 (0-59),没有更小的起点)

    • (2.) Dfs实现组合型枚举 + IDfs,我们枚举哪些公交路线叠加起来可以刚好填满所有的时刻。

    • (3.) 剪枝优化:

    1. 喜闻乐见 的优化枚举顺序:我们要尽可能地在每一次枚举公交路线的时候多影响一些点,那么我们可以按每一条公交路线影响的点从大到小排序

    2. 附加剪枝(很有用):如果我们还可以枚举 (k) 个公交路线,又因为我们已经把公交路线从大到小排序,假设当前我们从 (now) 开始枚举,(now) 这条公交路线有 (m) 个点,当前已经影响了 (s) 个点,总共有 (n) 个点,如果 (s+k*m < n) 的话,那么直接 return(因为最多还能影响 (k*m) 个点,加起来都还不够的话,这条分支就不能成功)

    我不会分析时间复杂度qwq,本来是组合型枚举的Dfs复杂度,加上剪枝后就跑得飞快了 %>_<%

    Code

    Talk is cheap.Show me the code.

    #include<bits/stdc++.h>
    using namespace std;
    inline int read() {
    	int x=0,f=1; char ch=getchar();
    	while(ch<'0' || ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
    	while(ch>='0'&&ch<='9') { x=(x<<3)+(x<<1)+(ch^48); ch=getchar(); }
    	return x * f;
    }
    const int N = 67, MAXN = 3607;
    int n,cnt,idt;
    int bus[N];
    struct Node {
    	int x,d,nums;
    	bool operator < (const Node &el) const {
    		return nums > el.nums;	//经典剪枝:优化搜索顺序,从大到小搜 
    	}
    }route[MAXN];	//route 线路 
    inline bool check(int x,int d) {
    	for(int i=x;i<60;i+=d)
    		if(!bus[i]) return false;
    	return true;
    }
    bool Dfs(int dep,int now,int res) {
    	if(dep == idt) return (res == n);
    	if(res+(idt-dep)*route[now].nums < n) return false;	//在线剪枝 
    	for(int i=now;i<=cnt;++i) {
    		int x = route[i].x, d = route[i].d, nums = route[i].nums;
    		if(check(x,d)) {
    			for(int j=x;j<60;j+=d) bus[j]--;
    			if(Dfs(dep+1,i,res+nums)) return true;
    			for(int j=x;j<60;j+=d) bus[j]++;
    		}
    	}
    	return false;
    }
    int main()
    {
    	n = read();
    	for(int i=1,pos;i<=n;++i) {
    		pos = read(); ++bus[pos];
    	}
    	for(int i=0;i<60;++i)
    		for(int j=i+1;i+j<60;++j)
    			if(check(i,j)) route[++cnt] = (Node)<%i,j,(59-i)/j+1%>;
    	//printf("test cnt = %d
    ",cnt);
    	sort(route+1, route+1+cnt);
    	idt = 0;
    	while(!Dfs(0,1,0)) {
    		++idt;
    		if(idt > 17) {
    			puts("Unkown Problems!");
    			return 0;
    		}
    	}
    	printf("%d
    ",idt);
    	return 0;
    }
    

    Summary

    认真看题,认真分析,认真思考!!!

    • 组合型枚举模型 Get

    • 等差数列 Get

    • 优化搜索顺序大法 Get

  • 相关阅读:
    React Native 实现MQTT 推送调研 (1)
    bpmn的依赖注入
    vdom diff
    浏览器渲染与event loop
    uni-app 通过后缀名区分不同渠道版本
    网页定宽 栅格布局
    网页顶部菜单导航和左侧菜单导航的区别?
    Android利用tcpdump和wireshark抓取网络数据包
    就算做了
    我眼中的Serverless
  • 原文地址:https://www.cnblogs.com/BaseAI/p/12181205.html
Copyright © 2020-2023  润新知