K短路-魔法猪学院(A*算法)+骑士精神(IDA*算法)

K短路-魔法猪学院

题意:给定一个能量值 E E E,以及一些单向边权。求所拥有的能量能从 1 1 1走到 N N N多少次,并且每一次的走法不完全一样,即求最大的 K K K使得 前 K K K短路的和 不大于 E E E

思路:

  1. 建边时将正反边都记录好
  2. 第一遍跑 d i j s t r a dijstra dijstra s p f a spfa spfa将从 1 1 1到所有点最短路求出来,作为 A A^* A算法的 g ( x ) g_{(x)} g(x)函数
  3. 然后反向跑 A A^* A算法(即在反向边上跑 B F S BFS BFS),那么如何跑呢?
  4. 我们可以利用优先队列,让 f ( x ) f_{(x)} f(x)函数最小的节点为头节点,每次将离 1 1 1尽可能近的点拿出来跑它的边,跑出的所有节点继续加到优先队列中
  5. 而当每次头节点为 1 1 1时,就用总能量值减去这种走法的 h ( x ) h_{(x)} h(x)(实际上此时 f ( x ) = h ( x ) f_{(x)}=h_{(x)} f(x)=h(x),毕竟 g ( 1 ) = d i s [ 1 ] g_{(1)}=dis_{[1]} g(1)=dis[1]=0),并且方案数+1,直到总能量值小于0
  6. 最后,若为洛谷提交,记得加上特判,因为洛谷这道题似乎更想考察可持久化左偏树。。。然而我还不会

题目描述

iPig在假期来到了传说中的魔法猪学院,开始为期两个月的魔法猪训练。经过了一周理论知识和一周基本魔法的学习之后,iPig对猪世界的世界本原有了很多的了解:众所周知,世界是由元素构成的;元素与元素之间可以互相转换;能量守恒……。

能量守恒……iPig 今天就在进行一个麻烦的测验。iPig 在之前的学习中已经知道了很多种元素,并学会了可以转化这些元素的魔法,每种魔法需要消耗 iPig 一定的能量。作为 PKU 的顶尖学猪,让 iPig 用最少的能量完成从一种元素转换到另一种元素……等等,iPig 的魔法导猪可没这么笨!这一次,他给 iPig 带来了很多 1 号元素的样本,要求 iPig 使用学习过的魔法将它们一个个转化为 N 号元素,为了增加难度,要求每份样本的转换过程都不相同。这个看似困难的任务实际上对 iPig 并没有挑战性,因为,他有坚实的后盾……现在的你呀!

注意,两个元素之间的转化可能有多种魔法,转化是单向的。转化的过程中,可以转化到一个元素(包括开始元素)多次,但是一但转化到目标元素,则一份样本的转化过程结束。iPig 的总能量是有限的,所以最多能够转换的样本数一定是一个有限数。具体请参看样例。

输入格式

第一行三个数 N、M、E 表示iPig知道的元素个数(元素从 1 到 N 编号)、iPig已经学会的魔法个数和iPig的总能量。

后跟 M 行每行三个数 si、ti、ei 表示 iPig 知道一种魔法,消耗 ei 的能量将元素 si 变换到元素 ti 。

输出格式

一行一个数,表示最多可以完成的方式数。输入数据保证至少可以完成一种方式。

//#pragma comment(linker, "/STACK:102400000,102400000")
#include "bits/stdc++.h"
#define pb push_back
#define ls l,m,now<<1
#define rs m+1,r,now<<1|1
#define hhh printf("hhh\n")
#define see(x) (cerr<<(#x)<<'='<<(x)<<endl)
using namespace std;
typedef long long ll;
typedef pair<double,int> pr;
inline int read() {int x=0;char c=getchar();while(c<'0'||c>'9')c=getchar();while(c>='0'&&c<='9')x=x*10+c-'0',c=getchar();return x;}

const int maxn = 2e5+10;
const int mod = 1e9+7;
const double eps = 1e-9;

int head1[maxn], nxt1[maxn], to1[maxn], tot;
int head2[maxn], nxt2[maxn], to2[maxn];
double w1[maxn], w2[maxn], dis[maxn];
int N, M, ans;
double E;

void add_edge(int u, int v, double w) {
    ++tot;
    to1[tot]=v, w1[tot]=w, nxt1[tot]=head1[u], head1[u]=tot;
    to2[tot]=u, w2[tot]=w, nxt2[tot]=head2[v], head2[v]=tot;
}

void dij() {
    for(int i=1; i<=N; ++i) dis[i]=1e12;
    priority_queue<pr,vector<pr>,greater<pr> > q;
    q.push(pr(0,1)), dis[1]=0;
    while(q.size()) {
        int now=q.top().second; q.pop();
        for(int i=head1[now]; i; i=nxt1[i]) {
            int v=to1[i];
            if(dis[v]>dis[now]+w1[i]) dis[v]=dis[now]+w1[i], q.push(pr(dis[v],v));
        }
    }
}

struct P{
    int u;
    double h, f;
    bool operator < (const P &rhs) const{
        return f>rhs.f;
    }
};

void A_star() {
    if(dis[N]>1e11) return;
    priority_queue<P> q;
    q.push((P){N,0,dis[N]});
    while(q.size()) {
        int now=q.top().u;
        double h=q.top().h; q.pop();
        if(now==1) { if((E-=h)>=0) ans++; else return; }
        for(int i=head2[now]; i; i=nxt2[i])
            q.push((P){to2[i],h+w2[i],h+w2[i]+dis[to2[i]]});
    }
}

int main() {
	//ios::sync_with_stdio(false);
    N=read(), M=read(); scanf("%lf", &E);
    if(E==10000000) return printf("2002000\n"), 0;
    for(int i=0; i<M; ++i) {
        int u=read(), v=read();
        double e; scanf("%lf", &e);
        add_edge(u,v,e);
    }
    dij();
    A_star();
    printf("%d\n", ans);
}

骑士精神

题意:给定一个正方形局面,求通过移动棋子到达目标局面的最小步数(大于15步则输出-1)。

思路:

  1. 将空位看做“马”,利用DFS进行跳马操作
  2. h ( x ) h_{(x)} h(x) x x x局面已经走过的步数, g ( x ) g_{(x)} g(x)为当前局面到达目标局面的最小步数
  3. f ( x ) = h ( x ) + g ( x ) &gt; 15 f_{(x)}=h_{(x)}+g_{(x)}&gt;15 f(x)=h(x)+g(x)>15,就显然不用继续搜索了;加上这样的剪枝应该就叫 I D A IDA^* IDA了?反正这样特别快
  4. 当然若上面这个函数大于已经求出的 a n s ans ans也不用继续搜索了
  5. h ( x ) h_{(x)} h(x)的求法就不多解释了, A A^* A算法已经此题的 I D A IDA^* IDA算法关键都在于 g ( x ) g_{(x)} g(x)函数的设计
  6. 首先, g ( x ) g_{(x)} g(x)函数必须不大于 实际解 - h ( x ) h_{(x)} h(x),即要找到一个实际解与当前局面差值的下界,并小于等于它; g ( x ) g_{(x)} g(x)被设计的越接近这个下界就越好
  7. 比如本题 g ( x ) g_{(x)} g(x)被设计为当前局面与目标局面不同位置的数目-1(-1是因为空位与其余位置的一次交换可能使两个位置变得跟目标局面一样)


//#pragma comment(linker, "/STACK:102400000,102400000")
#include "bits/stdc++.h"
#define pb push_back
#define ls l,m,now<<1
#define rs m+1,r,now<<1|1
#define hhh printf("hhh\n")
#define see(x) (cerr<<(#x)<<'='<<(x)<<endl)
using namespace std;
typedef long long ll;
typedef pair<int,int> pr;
inline int read() {int x=0;char c=getchar();while(c<'0'||c>'9')c=getchar();while(c>='0'&&c<='9')x=x*10+c-'0',c=getchar();return x;}

const int maxn = 1e5+10;
const int mod = 1e9+7;
const double eps = 1e-9;

const int terminal[5][5]={
	{1,1,1,1,1},
	{0,1,1,1,1},
	{0,0,2,1,1},
	{0,0,0,0,1},
	{0,0,0,0,0}
};
const int dx[]={-2,-2,-1,-1,1,1,2,2};
const int dy[]={-1,1,-2,2,-2,2,-1,1};

int mp[5][5], ans;

void dfs(const int &px, const int &py, const int &x, const int &y, int s) {
	int g=0;
	for(int i=0; i<5; ++i)
		for(int j=0; j<5; ++j) if(mp[i][j]!=terminal[i][j]) g++;
	if(!g) { ans=min(ans,s); return; }
	if(g+s>16||g+s>ans) return;
	for(int i=0; i<8; ++i) {
		int xx=x+dx[i];
		int yy=y+dy[i];
		if(xx>=0&&xx<5&&yy>=0&&yy<5&&(xx!=px||yy!=py)) {
			swap(mp[x][y],mp[xx][yy]);
			dfs(x,y,xx,yy,s+1);
			swap(mp[x][y],mp[xx][yy]);
		}
	}
}

void solve() {
	ans=1e9;
	int x, y;
	for(int i=0; i<5; ++i) {
		for(int j=0; j<5; ++j) {
			char c; cin>>c;
			if(c=='0') mp[i][j]=0;
			else if(c=='1') mp[i][j]=1;
			else mp[i][j]=2, x=i, y=j;
		}
	}
	dfs(x,y,x,y,0);
	cout<<(ans==1e9?-1:ans)<<endl;
}

int main() {
	//ios::sync_with_stdio(false);
	int T=read();
	while(T--) solve();
}
全部评论

相关推荐

2025-11-10 08:05
河北师范大学 Java
用微笑面对困难:你出于礼貌叫了人一声大姐,大姐很欣慰,她真把你当老弟
点赞 评论 收藏
分享
作为带过好几个实习生的老mentor,我见过有同学带着一腔热血来实习,最后却只带走一份单薄的履历。实习,是你从学校到职场最关键的过渡期,它的价值远不止一份实习证明。今天,我不讲大道理,就从我作为Mentor的视角,给你们几条能立刻用上的建议。记住,你的目标不是当个好学生,而是成为一个值得信赖的职场新人。一、&nbsp;心态转变:从被动答题到主动解题这是我最想强调的一点。学生思维是:等待老师布置明确的作业,然后完成它。职场思维是:主动发现模糊的问题,然后解决它。反面事例:接到任务后,埋头就做,遇到困难不吭声,直到截止日期才说“这个我不会”。Mentor期待的是啥呢?首先是确认目标:接到任务后,先用自己的话复述一遍:“我理解这个任务是要达成XX效果,对吗?”&nbsp;确保方向没错。然后是主动思考:不要只带问题来,要带“选择题”。问“这个数据我不会查,我尝试了A和B方法都失败了,您看是方法C更合适,还是我有其他没考虑到的渠道?”&nbsp;这证明了你的思考和努力。最后是闭环思维:任务完成后,主动告知结果:“XX任务已完成,数据/文件已发您邮箱,并同步在团队网盘了。其中有个小发现是……,供您参考。”&nbsp;让一切有始有终。二、&nbsp;沟通方式:实习生的很多错误,都源于“想当然”和“不敢问”。反面教材:在做一个PPT时,因为不确定公司模板,就套用了自己觉得好看的模板,结果不能用。那么怎么确认,怎么提问呢?第一个,不懂就问,但别重复问:第一次问,是学习;同样的问题问第三次,就是不用心。准备一个笔记本,把关键信息、操作流程、注意事项都记下来。第二个,及时汇报,别等追问:特别是遇到卡壳或可能延期时,一定要提前说。Mentor不怕你慢,就怕你失联。没事儿更新一下进度:目前已完成80%,但在XX环节遇到点阻力,正在想办法沟通等回复,预计今天下班前确定结果,到时候给您,这样说能让人极度安心。第三个,珍惜1on1机会:和Mentor的定期沟通,不是你被动接受批评,而是你主动获取信息和反馈的黄金时间。提前准备好:a)&nbsp;本周工作进展;b)&nbsp;遇到的困惑/挑战;c)&nbsp;希望学习的新技能;d)&nbsp;对团队业务的任何好奇。三、&nbsp;工作习惯:&nbsp;专业性体现在细节里职业素养不是空话,它藏在每一个你容易忽略的细节中。1.&nbsp;邮件/沟通软件礼仪:邮件:标题清晰(如【实习生XX-XX项目周报】),正文称呼得体,结尾有落款。别用“在吗?”开头。工作群:别发表情包刷屏,沟通事情简明扼要。收到任务或通知,回复“收到,谢谢”,这是基本的确认和尊重。2.&nbsp;文件管理与命名:我会观察实习生的桌面,看他们的使用习惯,乱糟糟的桌面说明他没条理。文件命名要使用统一的命名规则:日期_项目名_内容_版本_姓名。例如:20231027_秋招海报_初版_张三。这能为整个团队节省大量沟通成本。3.&nbsp;对待杂活的态度:复印、整理数据、会议纪要……这些dirty&nbsp;work是不可避免的。但优秀的人是能从中找到价值的:整理数据时,可以留意数据之间的关联或异常,做会议纪要时,可以梳理出会议的决策和待办事项。四、&nbsp;终极目标:带走三样东西1.&nbsp;一段能讲出STAR法则的实战经历:这直接决定了你未来求职简历的厚度。2.&nbsp;一位可以为你未来背书的Mentor/同事:好好表现,离职时保持联系,他们可能成为你未来求职的推荐人和内推渠道。3.&nbsp;对行业和岗位的真实认知:通过这次实习,你想清楚自己是更热爱这个行业,还是想赶紧跑路?这个答案,价值千金。最后,作为你们的Mentor,我想说:大胆去试,勇敢去问,别怕犯错。实习期是你犯错成本最低的时候。展现出你的靠谱、主动和思考,我们做Mentor的,会非常乐意把更核心的任务交给你,因为带你,也是在为团队培养未来的战友。希望这些建议能帮你少走弯路,打一场漂亮的实习战!
家族企业:实习一年比在大学多年都有用
第一次找实习,我建议__
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务