TOJ 1611 Moo University - Financial Aid -- 线段树 + DP

探讨了在ACM竞赛中解决一个特定的算法问题,该问题要求在满足一定条件下找到一组二元组中X的最大中位数值。通过动态规划与线段树实现了高效的解决方案。

题目链接:http://acm.tzc.edu.cn/acmhome/problemdetail.do?&method=showdetail&id=1611

题目大意:给定c个二元组(x, y),要求从中选出n(n为奇数)个,满足这n个二元组的y之和不超过给定的f,而且x的中位数最大。输出最大的中位数。

分析:先将二元组(记为cow)按X排序,然后枚举每个X作为中位数。这时需要在X之前和X之后各选n / 2个数,因为已经按X排序,不管怎么选都可以满足X是中位数的条件,所以只需要各自选取Y最小的n / 2个数,判断它们的和是否满足不超过f这个条件。如果满足,X可行,否则不行。如果按X从大到小枚举X,那么遇到的第一个可行X即为答案。剩下的问题在于如何高效地求出最小的n / 2个数的和。

可以用动态规划解决这个问题。用一个长为n / 2的数组a记录以cow[i].X为中位数时前面最小的n / 2个Y,同时用dp[i]表示这些数中的最大值的下标,那么计算dp[i + 1]时,如果cow[i + 1].Y >= a[dp[i]],则dp[i + 1] = dp[i]。否则用cow[i + 1].Y替换a[dp[i]],同时计算出新的最大值下标也就是dp[i + 1]。这涉及单点更新及全区间的最大值查询,用线段树可以实现。至于求和,在计算dp[i]的时候顺便就可以求出来了。对于cow[i].X之后的数,再进行一次同样的操作。下面的代码中的dp保存的就是n / 2个数的和,因为最大值下标可以直接从线段树中读出,不需要另外存储。求出dp之后再进行一次线性扫描就可以求出答案了。

#include 
  
   
#include 
   
    
#include 
    
     
#include 
     
      
#include 
      
       
#include 
        #include 
        
          #include 
         
           #include 
          
            #include 
           
             #include 
            
              #include 
             
               #include 
              
                #include 
               
                 #include 
                
                  #include 
                 
                   #define mp make_pair #define X first #define Y second #define MEMSET(a, b) memset(a, b, sizeof(a)) using namespace std; typedef unsigned int ui; typedef long long ll; typedef unsigned long long ull; typedef pair
                  
                    pii; typedef vector
                   
                     vi; typedef vi::iterator vi_it; typedef map
                    
                      mii; typedef priority_queue
                     
                       pqi; typedef priority_queue
                      
                       , greater
                       
                         > rpqi; typedef priority_queue
                        
                          pqp; typedef priority_queue
                         
                          , greater
                          
                            > rpqp; const int MAX_C = 100000 + 2; const int MAX_N = 10000 + 2; pii cow[MAX_C]; int a[MAX_N]; int dp[2][MAX_C]; int cnt; struct { int left; int right; int max_index; inline int mid() { return (left + right) >> 1; } } st[MAX_N * 4]; void build(int l, int r, int idx) { st[idx].left = l; st[idx].right = r; if (l == r) st[idx].max_index = cnt++; else { int mid = st[idx].mid(), lc = idx << 1, rc = lc | 1; build(l, mid, lc); build(mid + 1, r, rc); int lm = st[lc].max_index, rm = st[rc].max_index; st[idx].max_index = a[lm] > a[rm] ? lm : rm; } } void update(int pos, int idx) { if (st[idx].left != st[idx].right) { int mid = st[idx].mid(), lc = idx << 1, rc = lc | 1; if (pos <= mid) update(pos, lc); else update(pos, rc); int lm = st[lc].max_index, rm = st[rc].max_index; st[idx].max_index = a[lm] > a[rm] ? lm : rm; } } int main(int argc, char *argv[]) { // freopen("D:\\in.txt", "r", stdin); int n, c, f, i; cin >> n >> c >> f; for (i = 0; i < c; ++i) scanf("%d%d", &cow[i].X, &cow[i].Y); //先处理n = 1时的特殊情况 if (n == 1) { int ans = -1; for (int j = 0; j < c; ++j) if (cow[j].Y <= f) ans = max(ans, cow[j].X); cout << ans << endl; return 0; } sort(cow, cow + c); //计算dp[0] for (i = 0; i < (n >> 1); ++i) dp[0][n >> 1] += (a[i] = cow[i].Y); cnt = 0; build(0, (n >> 1) - 1, 1); for (; i < c - (n >> 1); ++i) { int mi = st[1].max_index; if (a[mi] > cow[i].Y) { dp[0][i + 1] = dp[0][i] - a[mi] + cow[i].Y; a[mi] = cow[i].Y; update(mi, 1); } else dp[0][i + 1] = dp[0][i]; } //计算dp[1] for (i = 0; i < (n >> 1); ++i) dp[1][c - (n >> 1) - 1] += (a[i] = cow[c - 1 - i].Y); cnt = 0; build(0, (n >> 1) - 1, 1); for (i = c - (n >> 1) - 1; i > (n >> 1); --i) { int mi = st[1].max_index; if (a[mi] > cow[i].Y) { dp[1][i - 1] = dp[1][i] - a[mi] + cow[i].Y; a[mi] = cow[i].Y; update(mi, 1); } else dp[1][i - 1] = dp[1][i]; } for (i = c - (n >> 1) - 1; i >= (n >> 1); --i) if (dp[0][i] + dp[1][i] + cow[i].Y <= f) break; if (i >= (n >> 1)) cout << cow[i].X << endl; else cout << -1 << endl; return 0; } 
                          
                         
                        
                       
                      
                     
                    
                   
                  
                 
                
               
              
             
            
           
          
         
        
      
     
    
   
  

内容概要:本研究聚焦于绿电直连型电氢氨园区的优化运行,提出一种集成绿色电力直接供给、电解水制氢及氢气合成氨工艺的综合能源系统架构。通过建立包含风光发电、电解槽、氨合成反应器、储氢罐、电网交互及多类型负荷在内的系统模型,综合考虑绿电直供优先、能量梯级利用与多能互补原则,构建以系统综合运行成本最小化为目标的优化调度模型。研究采用Matlab与Python工具进行算法解和仿真分析,利用实际气象与负荷数据完成案例验证,评估了不同运行策略下系统的经济性、可再生能源消纳能力与碳减排效益,为新型电氢氨一体化园区的规划与运行提供了理论依据和技术支撑。; 适合人群:具备一定电力系统、新能源或化工背景的研究生、科研人员及从事综合能源系统规划与优化工作的工程技术人员。; 使用场景及目标:①用于科研学习,理解电--氨多能转换系统的建模与优化方法;②为工业园区的低碳化、智能化改造提供技术参考与决策支持;③作为开发类似综合能源管理系统的理论基础。; 阅读建议:此资源包含完整的模型代码、数据与论文,使用者应结合代码仔细研读论文中的模型构建部分,重点关注目标函数与约束条件的设计逻辑,并尝试修改参数进行仿真,以深入掌握优化算法在实际系统中的应用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值