问题理解

晨晨有 n 只兔子在花园里捣乱,每只兔子有两个属性:

tᵢ:把这只兔子送回笼子所需的时间(单程),来回一趟需要 2 × tᵢ​ 时间。

dᵢ:这只兔子每单位时间能破坏的花朵数。

送兔子的顺序可以任意选择。在送某只兔子的过程中(耗时 2tᵢ),其他所有尚未被抓的兔子会持续破坏花园。晨晨希望安排一个顺序,使得总破坏花朵数最小。

关键思路:贪心排序

这个问题不能用简单的“先抓破坏力大的”或“先抓耗时短的”来解决,因为两者相互制约。我们需要比较两只兔子谁更应该先抓。

假设只有两只兔子 A 和 B,考虑两种顺序:

先 A 后 B​

送 A 时,B 还在破坏,破坏值 = d_B × 2t_A

送 B 时,已经没有其他兔子了,破坏值 = 0

总破坏 = d_B × 2t_A

先 B 后 A​

送 B 时,A 还在破坏,破坏值 = d_A × 2t_B

总破坏 = d_A × 2t_B

哪种顺序更优?比较两个总破坏:

先 A 后 B 更优 ⇔ d_B × 2t_A < d_A × 2t_B

两边同时除以 2,得 d_B × t_A < d_A × t_B​

移项得 d_A / t_A > d_B / t_B(注意 t 为正数)

结论:按照 dᵢ / tᵢ 从大到小排序,比值大的兔子应该先抓。为了避免浮点数误差,实际比较时使用交叉相乘:d_A × t_B > d_B × t_A则 A 排在 B 前面。

计算总破坏

排序后,我们按顺序送兔子。设当前还未被抓的兔子的破坏力总和为 sum(初始为所有 dᵢ 之和)。当送第 i 只兔子时:

耗时 2 × tᵢ

在此期间,其他 sum - dᵢ只兔子(即剩下所有兔子)一直在破坏

破坏增加 = (sum - dᵢ) × 2 × tᵢ

送完这只兔子后,从 sum 中减去它的 dᵢ,继续处理下一只。

样例验证

输入:

复制 6 3 1 2 5 2 3 3 2 4 1 1 6

先计算每只兔子的 d/t 比值(近似):

兔子1: 1/3 ≈ 0.33

兔子2: 5/2 = 2.5

兔子3: 3/2 = 1.5

兔子4: 2/3 ≈ 0.67

兔子5: 1/4 = 0.25

兔子6: 6/1 = 6

按比值降序:6 > 2 > 3 > 4 > 1 > 5,与样例顺序一致。

计算过程(sum 初始 = 1+5+3+2+1+6 = 18):

送6:耗时2×1=2,破坏 = (18-6)×2 = 24,sum=12

送2:耗时2×2=4,破坏 = (12-5)×4 = 28,sum=7

送3:耗时2×2=4,破坏 = (7-3)×4 = 16,sum=4

送4:耗时2×3=6,破坏 = (4-2)×6 = 12,sum=2

送1:耗时2×3=6,破坏 = (2-1)×6 = 6,sum=1

送5:耗时2×4=8,破坏 = (1-1)×8 = 0,sum=0

总破坏 = 24+28+16+12+6 = 86 ✅ 上一期代码

#include<bits/stdc++.h>
using namespace std;
int mp[6][6],vis[15][15],a[4]={1,0,-1,0},b[4]={0,1,0,-1};
int peth[255][2];
int n,m,ans;
char as[110][110];
void dfs(int x,int y,int step){
	if(x==n&&y==m){
		ans++;
		cout<<ans<<":";
		for(int i=0;i<step-1;i++){
			cout<<peth[i][0]<<","<<peth[i][1]<<"->";
		}
		cout<<peth[step-1][0]<<","<<peth[step-1][1]<<endl;
	}
	for(int i=0;i<4;i++){
		int xx=x+b[i];
		int yy=y+a[i];
		if(xx<=n&&xx>=1&&yy<=m&&yy>=1&&vis[xx][yy]==0&&as[xx][yy]=='o'){
			peth[step][0]=xx;
            peth[step][1]=yy;
			vis[xx][yy]=1;
			dfs(xx,yy,step+1);
			vis[xx][yy]=0;
		}
	}
}
int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			cin>>as[i][j];
		}
	}
	vis[1][1]=1;
	peth[0][0]=1;
    peth[0][1]=1;
    dfs(1,1,1);
    if(ans==0) cout<<"no";
	return 0;
}

老规矩,下一期将颁布代码,同时预告时间为2026.7.25,内容是高精度c++内容

2 条评论

  • 1