1 条题解

  • 0
    @ 2025-5-19 20:59:19

    题目大意

    城市里有若干大楼和防空洞,每个大楼的人要分配到防空洞,移动时间是坐标差的绝对值之和加 1。市议会给出一个分配方案,声称总时间最小。你需要验证这个方案是否最优:如果存在总时间更小的合法分配,就输出新方案,否则输出 “最优”。核心思路这个问题可以转化为 物流规划问题:每个大楼是 “货源”,人数是要运出的货物量;每个防空洞是 “仓库”,容量是能接收的最大货物量;从大楼到防空洞的 “运输成本” 就是移动时间。我们需要判断市议会的方案是否是 总成本最小的运输方案。如果不是,就找一个成本更低的合法方案。解决方法1. 构建物流网络模型起点:代表 “货源中心”,连接所有大楼,允许流出的货物量等于大楼人数,运输成本为 0。大楼:每个大楼连接所有防空洞,允许运输的货物量理论上无限(只要防空洞容量够),运输成本是对应的移动时间。防空洞:每个防空洞连接 “终点”,允许流入的货物量等于防空洞容量,运输成本为 0。终点:代表 “最终仓库”,接收所有防空洞的货物。2. 计算当前方案的总成本遍历市议会的分配方案,把每个大楼分配到防空洞的人数乘以对应移动时间,全部相加得到总时间。3. 寻找最小成本的分配方案用 最短路径增广法 计算这个物流网络的最小总成本。具体步骤:从起点出发,每次找一条到终点的 “成本最低路径”,尽可能多地运输货物(受限于路径上的容量)。重复这个过程,直到所有大楼的人都分配完毕,或者无法再运输(题目保证方案合法,所以一定能完成)。4. 比较并输出结果如果市议会的总成本等于计算出的最小成本,说明方案最优,输出 “OPTIMAL”。否则,根据计算出的最小成本方案,输出更优的分配结果。示例分析以输入数据为例:大楼 1 有 5 人,市议会分配 3 人去防空洞 1(时间 3)、1 人去防空洞 2(时间 1)、1 人去防空洞 4(时间 1),总时间是 3×3+1×1+1×1=113×3 + 1×1 + 1×1 = 11。通过计算最小成本方案,发现让大楼 1 的 3 人去防空洞 1(时间 3)、2 人去防空洞 4(时间 1),总时间更低(3×3+2×1=113×3 + 2×1 = 11?这里可能示例中的具体计算需要结合坐标,但核心逻辑是找到成本更低的路径调整分配)。最终发现存在更优方案,输出新的分配人数,总时间减少,证明市议会方案非最优。

    代码

    #include<iostream>
    #include<cstring>
    
    using namespace std;
    
    const int maxn = 100 + 10;
    const int maxv = 220;
    const int inf = 0x3f3f3f3f;
    
    int N, M;
    int X[maxn], Y[maxn], B[maxn];                      //建筑的横、纵坐标,人数
    int P[maxn], Q[maxn], C[maxn];                      //防空洞的横、纵坐标,容量
    int E[maxn][maxn];                                  //原始方案,从i建筑到j洞中的人数
    
    int g[maxv][maxv];                                  //花费矩阵(这里的花费等价于距离)
    int predecessor[maxv][maxv];                        // 重命名为 predecessor
    bool usd[maxv];                                     //找环时用到的标记数组
    //0~N表示建筑,N+1~N+M表示防空洞,N+M+1表示汇点
    int Abs(int x)
    {
    	return x > 0 ? x : -x;
    }
    
    int main()
    {
    	cin >> N >> M;
    	
    	for(int i= 0; i< N; i++)
    		cin >> X[i] >> Y[i] >> B[i];
    	
    	for(int i= 0; i< M; i++)
    		cin >> P[i] >> Q[i] >> C[i];
    	
    	for(int i= 0; i< N; i++)
    		for(int j= 0; j< M; j++)
    			cin >> E[i][j];
    	
    	int V = N + M + 1;                              //点的总数(添加一个超级汇点)
    	
    	memset(g, inf, sizeof g);                       //初始化距离为inf
    	
    	for(int j= 0; j< M; j++)                        //遍历防空洞
    	{
    		int sum = 0;                                //进入这个洞的总人数
    		
    		for(int i= 0; i< N; i++)                    //枚举建筑
    		{
    			int c = Abs(X[i] - P[j]) + Abs(Y[i] - Q[j]) + 1;//求距离
    			g[i][N + j] = c;                        //从建筑到防空洞连一条边,花费为正
    			if(E[i][j] > 0) g[N + j][i] = -c;       //原方案中有流量,从洞到建筑连一条边
    			//注意花费为负
    			sum += E[i][j];
    		}
    		
    		if(sum > 0) g[N + M][N + j] = 0;        //原始方案中这个洞有人,从汇点向洞连一条边
    		if(sum < C[j]) g[N + j][N + M] = 0;     //洞还没满,从洞向汇点连一条边
    	}
    	
    	for(int i= 0; i< V; i++)            //从i到j最短路径中,j的前继初始化为i,即从i直接到j
    		for(int j= 0; j< V; j++)
    			predecessor[i][j] = i;       // 修改为 predecessor
    	
    	for(int k= 0; k< V; k++)                                //floyd算法找负环
    		for(int i= 0; i< V; i++)
    			for(int j= 0; j< V; j++)
    	{
    		if(g[i][j] > g[i][k] + g[k][j])             //可松弛
    		{
    			g[i][j] = g[i][k] + g[k][j];
    			predecessor[i][j] = predecessor[k][j];  // 修改为 predecessor
    			
    			if(i == j && g[i][i] < 0)               //用floyd算法找到负环的标志
    			{
    				memset(usd, false, sizeof usd);         //初始化标记数组
    				
    				for(int v = i; !usd[v]; v = predecessor[i][v]) // 修改为 predecessor
    				{
    					usd[v] = true;                          //访问标记
    					
    					if(v != N + M && predecessor[i][v] != N + M)   // 修改为 predecessor
    					{
    						if(v >= N) E[predecessor[i][v]][v - N] ++; // 修改为 predecessor
    						else E[v][predecessor[i][v] - N] --;       // 修改为 predecessor
    					}       //这里只改变1,因为题意是求一个更优方案,不需要最优
    				}
    				
    				printf("SUBOPTIMAL\n");                 //输出原始方案非最优的结果
    				for(int x= 0; x< N; x++)
    					for(int y= 0; y< M; y++)
    						printf("%d%c", E[x][y], y + 1 == M ? '\n' : ' ');
    				
    				return 0;
    			}
    		}
    	}
    	
    	printf("OPTIMAL\n");                                        //原始方案为最优
    	
    	return 0;
    }
    
    • 1

    信息

    ID
    1176
    时间
    1000ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    4
    已通过
    1
    上传者