zoukankan      html  css  js  c++  java
  • CF::Gym 100113K

    CF::Gym题目页面传送门

    (n)组任务,每组(m_i)个,第(i)组第(j)个任务的编号为(sumlimits_{k=1}^{i-1}m_k+j)。第(i)个任务需要(a_i)个单位时间完成。刚开始时刻为(0),每完成一个任务(i)当前的时刻就增加(a_i)。若第(i)个任务完成的时刻为(t),则受到的处罚为(b_it)。从开始做一个任务到结束不允许做其他任务,从开始做某组任务到结束不允许做其他组的任务。求一个最优的完成任务顺序,使得受到的总处罚最小。输出最小总处罚和顺序。若有多解,输出任一。

    对于任何一个任务组的顺序,显然在此顺序下使得总处罚最小的每组组内顺序是不变的。于是考虑先求出每组组内最优顺序,再求任务组的最优顺序。

    像这种求最优顺序的题,往往都有一个套路。对于每组(i),考虑交换任意一种顺序(ord)下任意一个相邻对((ord_x,ord_{x+1})),使它变成((ord_{x+1},ord_x)),会不会更优。显然,(ord_{1sim x-1},ord_{x+2sim m_i})对总处罚的贡献和不会因为(ord_x,ord_{x+1})交换而变化,我们只需要关心(ord_x,ord_{x+1})对总处罚的贡献和。设(sum=sumlimits_{j=1}^{x-1}a_{ord_j}),则(ord_x,ord_{x+1})顺序下它们对总处罚的贡献和为(b_{ord_x}(sum+a_{ord_x})+b_{ord_{x+1}}(sum+a_{ord_x}+a_{ord_{x+1}})=b_{ord_x}sum+b_{ord_x}a_{ord_x}+b_{ord_{x+1}}sum+b_{ord_{x+1}}a_{ord_x}+b_{ord_{x+1}}a_{ord_{x+1}}=(b_{ord_x}+b_{ord_{x+1}})sum+a_{ord_x}b_{ord_x}+a_{ord_{x+1}}b_{ord_{x+1}}+a_{ord_x}b_{ord_{x+1}});类似地,可以求出(ord_{x+1},ord_x)顺序下对总处罚的贡献和为((b_{ord_x}+b_{ord_{x+1}})sum+a_{ord_x}b_{ord_x}+a_{ord_{x+1}}b_{ord_{x+1}}+a_{ord_{x+1}}b_{ord_x})。当((b_{ord_x}+b_{ord_{x+1}})sum+a_{ord_x}b_{ord_x}+a_{ord_{x+1}}b_{ord_{x+1}}+a_{ord_x}b_{ord_{x+1}}>(b_{ord_x}+b_{ord_{x+1}})sum+a_{ord_x}b_{ord_x}+a_{ord_{x+1}}b_{ord_{x+1}}+a_{ord_{x+1}}b_{ord_x}),即(a_{ord_x}b_{ord_{x+1}}>a_{ord_{x+1}}b_{ord_x})时,显然可以交换(ord_x,ord_{x+1})得到更优策略。我们将这样在一个顺序内可以交换使得策略更优的相邻对称为“相邻逆序对”。

    显然,有相邻逆序对的顺序一定不是最优的。我们暂且认为没有相邻逆序对的顺序一定是最优的。于是我们定义一个比较器(cmp(x,y)=[a_xb_y<a_yb_x]),按照它将该组内所有元素排序,这样得到的顺序一定是最优的。([a_xb_y<a_yb_x])可以变形成(left[dfrac{a_x}{b_x}<dfrac{a_y}{b_y} ight]),这样一来,比较器(cmp)的合法性显然。此时,“没有相邻逆序对的顺序一定是最优的”这个结论正确性也很显然了:排完序后,若(dfrac{a_x}{b_x}=dfrac{a_y}{b_y}),则(x)(y)一定相邻,任意(2)个有序的顺序一定可以通过重新排列每个相等段互相到达,此时易证所有有序顺序的总处罚相等,得证。

    求出了每组组内的最优顺序,现在来求任务组的最优顺序。上面是默认每组的开始时间为(0)的,而现在不一定。设(sum\_a_i=sumlimits_{j=sumlimits_{k=1}^{i-1}m_k+1}^{sumlimits_{k=1}^im_k}a_j,sum\_b_i=sumlimits_{j=sumlimits_{k=1}^{i-1}m_k+1}^{sumlimits_{k=1}^im_k}b_j)。对于一个顺序(ord),第(ord_i)组的开始时间显然是(sumlimits_{j=1}^{i-1}sum\_a_{ord_j}),这比开始时间为(0)对总处罚的贡献增加了(sum\_b_{ord_i}sumlimits_{j=1}^{i-1}sum\_a_{ord_j})。于是,我们可以把每个任务组看成一个大任务,按照与上面类似的方法定义一个比较器(cmp'(x,y)=left[sum\_a_xsum\_b_y<sum\_a_ysum\_b_x ight])并按照它将所有任务组排序,即可得到最优顺序。

    下面是AC代码:

    #include<bits/stdc++.h>
    using namespace std;
    #define int long long
    #define pb push_back
    const int N=500,M_I=100;
    int n;//任务组数 
    int m[N+1];//每组任务个数 
    int a[N+1][M_I+1]/*所需时间*/,b[N+1][M_I+1]/*处罚系数*/;
    int sum_a[N+1]/*该组内所有任务所需时间之和*/,sum_b[N+1]/*该组内所有任务处罚系数之和*/;
    int now;//当前正在处理的任务组 
    bool cmp(int x,int y){return a[now][x]*b[now][y]<a[now][y]*b[now][x];}//给组内任务排序的比较器 
    bool cmp0(int x,int y){return sum_a[x]*sum_b[y]<sum_a[y]*sum_b[x];}//给任务组排序的比较器 
    vector<int> ord[N+1]; 
    signed main(){
    	freopen("student.in","r",stdin);freopen("student.out","w",stdout);
    	cin>>n;
    	for(int i=1;i<=n;i++)cin>>m[i];
    	for(int i=1;i<=n;i++)for(int j=1;j<=m[i];j++)cin>>a[i][j];
    	for(int i=1;i<=n;i++)for(int j=1;j<=m[i];j++)cin>>b[i][j];
    	int ans=0;
    	int cnt=0;
    	for(int i=1;i<=n;i++){
    		for(int j=1;j<=m[i];j++)sum_a[i]+=a[i][j],sum_b[i]+=b[i][j];//计算sum_a,sum_b 
    		vector<int> v(m[i]); 
    		for(int j=1;j<=m[i];j++)v[j-1]=j;
    		now=i;
    		sort(v.begin(),v.end(),cmp);//排序 
    		int tim=0;
    		for(int j=0;j<m[i];j++)ans+=b[i][v[j]]*(tim+=a[i][v[j]]);//贡献答案 
    		for(int j=0;j<m[i];j++)ord[i].pb(cnt+v[j]);//记录最优顺序 
    		cnt+=m[i];
    	}
    	vector<int> v(n);
    	for(int j=1;j<=n;j++)v[j-1]=j;
    	sort(v.begin(),v.end(),cmp0);//排序 
    	int tim=0;
    	for(int i=0;i<n;i++)ans+=tim*sum_b[v[i]],tim+=sum_a[v[i]];//贡献答案 
    	cout<<ans<<"
    ";//输出答案 
    	for(int i=0;i<n;i++)for(int j=0;j<m[v[i]];j++)cout<<ord[v[i]][j]<<" ";//输出顺序 
    	return 0;
    }
    
  • 相关阅读:
    四则运算
    实验四 决策树算法及应用
    实验三 朴素贝叶斯算法及应用
    实验二 K-近邻算法及应用
    实验三 面向对象分析与设计
    实验二 结构化分析与设计
    实验一 软件开发文档与工具的安装与使用
    ATM管理系统
    流程图与活动图的区别与联系
    四则运算自动生成程序
  • 原文地址:https://www.cnblogs.com/ycx-akioi/p/CF-Gym-100113K.html
Copyright © 2011-2022 走看看