您的位置:首页 > 其它

Codeforces Round #433 div2 C,D 题 题解

2017-09-08 16:27 330 查看
CF – 835A 传送门

//题意: 给定n,k. 重新安排一个时间表, 范围是(k+1,k+n), 你需要计算的是一个新的时刻表, 使得res = (第一个时间 -1)*a[1] + (第二个时间 -2)*a[2] + … 要最小. a[1] - a
是题目中给出的.

//思路: 也是比较简单的一道题, 我们贪心的选择对于输入的cost越大的, 我们就从(k+1, k+n)中选出一个最接近对应这个cost的pos的数给它(这样减出来的值很小, 再一乘也就比较小了). 那么这样选出来的序列是最优的. (这个贪心的策略一定是对的, 你可以假设cost稍微变化下, 但是最后发现还是这样就是最优的). 那么如何在(k+1,k+n)中找到对应最接近的pos了, 并且要保证是按val值从大到小排好的. 那么自然就想到用优先队列维护. 对于当前k+i所有小于它的pos, 全部推入队列. 然后取出对首, 对首这个位置就应该是当前的k+i值. 然后顺便计算res即可. 细节请看代码

AC Code

/** @Cain*/
const int maxn = 3e5+5;
int cas=1;
int ans[maxn];
struct node
{
int val,pos;
bool operator < (const node& a) const {
return a.val > val;
}
}s[maxn];

void solve()
{
int n,k;
scanf("%d%d",&n,&k);
priority_queue<node >q;
for(int i=1;i<=n;i++){
scanf("%d",&s[i].val);
s[i].pos = i;
}
ll res = 0;
int cnt = 1;
//printf("%d\n",q.top().val);
for(int i=1;i<=n;i++){
while(cnt<=n && s[cnt].pos<=i+k){
q.push(s[cnt]);
cnt++;
}
node tmp = q.top();
q.pop();
ans[tmp.pos] = i+k;
res += 1ll*(i+k - tmp.pos)*tmp.val;
}
printf("%lld\n",res);
for(int i=1;i<=n;i++){
printf("%d%c",ans[i],i==n?'\n':' ');
}
}


CF - 835B 传送门

//题意: 有n个人, m条航班, 问让这n个人一起在首都0工作最少k天, 并最后都回家的最小花费(坐飞机去或回家, 到首都的当天不能算为工作日). 如果不可能就输出-1.

//思路: 先处理出每一个可以使所有人都到首都的天数的最优值. 再处理出每一个可以使所有人都回到家的天数的最优值. 然后扫一遍判断一下长度为k+1的前后缀的值的关系. 更新答案即可.

注意一点的就是: 处理完了前或后缀, 需要更新每一个天数的最小值. (前缀的思想!) 还有k的范围, 所以数组需要开大点! 细节请看代码.

AC Code

/** @Cain*/
const int maxn = 1e6+5;
int cas=1;
struct node
{
int d,s,e;
ll w;
bool operator < (const node& a) const {
return d<a.d;
}
}s[maxn];

ll dp1[2*maxn],dp2[2*maxn];
ll vis[maxn];
void solve()
{
int n,m,k;
while(~scanf("%d%d%d",&n,&m,&k)){
Fill(dp1,0); Fill(dp2,0);
int cnt1 = 0,cnt2 = 0 ;
int maxx = 0;
for(int i=1;i<=m;i++){
scanf("%d%d%d%lld",&s[i].d,&s[i].s,&s[i].e,&s[i].w);
maxx = max(maxx,s[i].d);
}
sort(s+1,s+1+m);
int num = 0;
ll sum = 0;
Fill(vis,0);
for(int i=1;i<=m;i++){  //处理前面
if(s[i].s == 0) continue;
if(!vis[s[i].s]){
num++;
vis[s[i].s] = s[i].w;
sum += s[i].w;
}
else if(vis[s[i].s] > s[i].w){
sum -= vis[s[i].s];
sum += s[i].w;
vis[s[i].s] = s[i].w;
}
if(num == n){  //n个人都到了首都.
dp1[s[i].d] = sum;
}
}
num = 0 ; sum = 0;
Fill(vis,0);
for(int i=m;i>=1;i--){  //处理后面
if(s[i].e == 0) continue;
if(!vis[s[i].e]){
num++;
vis[s[i].e] = s[i].w;
sum += s[i].w;
}
else if(vis[s[i].e] > s[i].w){
sum -= vis[s[i].e];
sum += s[i].w;
vis[s[i].e] = s[i].w;
}
if(num == n){  //n个人都回到了家
dp2[s[i].d] = sum;
}
}
for(int i=1;i<=maxx;i++){    //前缀的思想
if(!dp1[i]) dp1[i] = dp1[i-1];
else if(dp1[i-1]) dp1[i] = min(dp1[i],dp1[i-1]);
//如果第i-1天就全部到达了首都, 那么第i天肯定也全部都到了首都.需要更新一个最优值.
}
for(int i=maxx;i>=1;i--){
if(!dp2[i]) dp2[i] = dp2[i+1];
else if(dp2[i+1]) dp2[i] = min(dp2[i],dp2[i+1]);
//和前面的思想一样的.
}
ll res = INF;
for(int i=1;i<=maxx;i++){
if(dp1[i] && dp2[i+k+1]) res = min(res,dp1[i]+dp2[i+k+1]);
//每次判断一个长度为k的区间范围.同时更新答案.
}
if(res == INF) printf("-1\n");
else printf("%lld\n",res);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: