2014湘潭全国邀请赛I题 Intervals /POJ 3680 / 在限制次数下取有权区间使权最大/小问题(费用流)
先说POJ3680:给n个有权(权<10w)开区间(n<200),(区间最多数到10w)保证数轴上所有数最多被覆盖k次的情况下要求总权最大,输出最大权。
思路: 限制的处理:s-->开始流量为k,要求总权最大,即费用最大,所以费用取负,最小费用最大流即可。对于输入区间[a,b]:w,添加边:a-->b,流量为1,费用为-w。
对于点i,i+1,添加边,费用为0,流量无穷。显然这种处理,限制了区间最多取k次,(流量控制),跑最大流能走添加的边尽量走,且越大越好(负数刚刚是最小费用),满足题意。但是TLE,因为w到10W,边10W,必然超时 ,所以点要处理, 所有点“压缩”,向前推进,只要存在的点,前一个向后一个连边即可,详见代码。
再看湘潭的这题: 问题相反:给n个有权(权<10w)开区间(n<2000),(区间最多数到10的9次),保证区间【1-,m】最少被覆盖k次的情况下要求总权最小,输出最小权。
思路:(感激zz1215的建图提示) 限制的处理:显然在出口处流量必需达到k才算有解。对于输入区间[a,b]:w,添加边:a-->b,流量为1,费用为w,但是这样处理,点都是离散的,根本没有体现连续性,
不可能像上题那样建图(否则费用为0),所以:这样:点I向它前一个点连边,费用为0,流量为无穷,这样巧妙的解决了离散点问题。跑最小费用即可。显然,之前要处理点。
#include<iostream>
#include<queue>
#include<cstdio>
using namespace std;
const int inf=0x3f3f3f3f;
const int t=100000;
int n,k;
int e[300001][4];int head[100101];int nume=0;
void inline adde(int from,int to,int w,int c)
{
e[nume][0]=to;e[nume][1]=head[from];head[from]=nume;
e[nume][2]=w;e[nume++][3]=c;
e[nume][0]=from;e[nume][1]=head[to];head[to]=nume;
e[nume][2]=-w;e[nume++][3]=0;
}
int inq[111005];int d[110000]; //spfa
bool spfa(int & sumcost) //每次求费用
{
int pre[110005];
int minf=inf;
int prv[110005];
for(int i=0;i<=t+1;i++)
{
inq[i]=0;d[i]=inf;
}
pre[0]=-1 ; prv[0]=-1; //路径中,分别记录到点i的边,和i之前的点。(这题如果用矩阵建图要方便)
queue<int>q;
q.push(0);inq[0]=1;d[0]=0;
while(!q.empty())
{
int cur=q.front();
q.pop();
inq[cur]=0;
for(int i=head[cur];i!=-1;i=e[i][1])
{
int v=e[i][0];
if(e[i][3]>0&&e[i][2]+d[cur]<d[v])
{
d[v]=e[i][2]+d[cur];
prv[v]=cur; //记录增广路
pre[v]=i;
if(!inq[v])
{
q.push(v);
inq[v]=1;
}
}
}
}
if(d[t+1]==inf)return 0; //无法增广
int cur=t+1; //目的点
while(cur!=0) //取路径上最小残流量边作为流量增广
{
minf=e[pre[cur]][3]<minf?e[pre[cur]][3]:minf;
cur=prv[cur];
}
cur=t+1;
while(cur!=0) //增广,改流量
{
e[pre[cur]][3]-=minf;
e[pre[cur]^1][3]+=minf;
cur=prv[cur];
}
sumcost+=d[t+1]*minf; //费用为单位费用(该路径下每条边单位流量之和)*流量
return 1;
}
void mincost(int & sumcost)
{
while(spfa(sumcost)) ; //无法增广为止
return ;
}
int hash[100011];
void clear()
{
nume=0;
for(int i=0;i<=t+1;i++)
{
hash[i]=head[i]=-1;
}
}
struct qujian
{
int a,b,w;
};
int main()
{
int T;
scanf("%d",&T);
for(int ii=1;ii<=T;ii++)
{
clear();
cin>>n>>k;
int a,b,w;
vector<qujian>qq(n);
vector<int>v;
for(int i=0;i<n;i++)
{
scanf("%d%d%d",&a,&b,&w);
hash[a]=hash[b]=1;
qq[i].a=a;qq[i].b=b;qq[i].w=w;
adde(a,b,-w,1);
}
for(int i=0;i<100010;i++) //处理“存在”的点
if(hash[i]==1)
{
v.push_back(i);
}
for(int i=0;i<v.size()-1;i++) //“存在”的点连边
{
adde(v[i],v[i+1],0,k);
}
adde(v[v.size()-1],t,0,k); //超级源汇点的边
adde(0,v[0],0,k);adde(t,t+1,0,k);
int sumcost=0;
mincost(sumcost);
cout<<-sumcost<<endl; //相反数
}
return 0;
}
湘潭:
#include<iostream>
#include<queue>
#include<algorithm>
using namespace std;
const int inf=0x3f3f3f3f;
int n,k,m;
int countv=0;
int f[4009];
void getf(int x) // 把点1到10的9次(最多4000个点),压缩到4000以内,一一对应,以便建图。
{
if(x>m){f[countv]=x;return ;} //大于m的数没用,相当于m。
countv++;
f[countv]=x;
}
int getv(int x) //获取对应点
{
if(x>=m){return countv;}
for(int i=1;i<=countv;i++)
{
if(f[i]==x)return i;
}
}
int e[20001][4];int head[4005];int nume=0;
void inline adde(int from,int to,int w,int c)
{
e[nume][0]=to;e[nume][1]=head[from];head[from]=nume;
e[nume][2]=w;e[nume++][3]=c;
e[nume][0]=from;e[nume][1]=head[to];head[to]=nume;
e[nume][2]=-w;e[nume++][3]=0;
}
int inq[4005];int d[4005]; //spfa
bool spfa(int & sumcost,int &sumflow) //每次求费用
{
int pre[4005];
int minf=inf;
int prv[4005];
for(int i=0;i<=countv+4;i++)
{
inq[i]=0; d[i]=inf;
}
pre[0]=-1 ; prv[0]=-1; //路径中,分别记录到点i的边,和i之前的点。(这题如果用矩阵建图要方便)
queue<int>q;
q.push(0);inq[0]=1;d[0]=0;
while(!q.empty())
{
int cur=q.front();
q.pop();
inq[cur]=0;
for(int i=head[cur];i!=-1;i=e[i][1])
{
int v=e[i][0];
if(e[i][3]>0&&e[i][2]+d[cur]<d[v])
{
d[v]=e[i][2]+d[cur];
prv[v]=cur; //记录增广路
pre[v]=i;
if(!inq[v])
{
q.push(v);
inq[v]=1;
}
}
}
}
if(d[countv+1]==inf)return 0; //无法增广
int cur=countv+1; //目的点
while(cur!=0) //取路径上最小残流量边作为流量增广
{
minf=e[pre[cur]][3]<minf?e[pre[cur]][3]:minf;
cur=prv[cur];
}
cur=countv+1;
while(cur!=0) //增广,改流量
{
e[pre[cur]][3]-=minf;
e[pre[cur]^1][3]+=minf;
cur=prv[cur];
}
sumcost+=d[countv+1]*minf; //费用为单位费用(该路径下每条边单位流量之和)*流量
sumflow+=minf;
return 1;
}
void mincost(int & sumcost,int & sumflow)
{
while(spfa(sumcost,sumflow)) ; //无法增广为止
return ;
}
void clear()
{
nume=countv=0;
for(int i=0;i<=4003;i++)
{
head[i]=-1;
f[i]=0;
}
}
struct qujian
{
int a,b,w;
};
int main()
{
int T;
cin>>T;
for(int ii=1;ii<=T;ii++)
{
clear();
cin>>n>>k>>m;
int a,b,w;
vector<int>v;
vector<qujian>qq(n);
for(int i=0;i<n;i++)
{
cin>>a>>b>>w;
v.push_back(a);
v.push_back(b);
qq[i].a=a;qq[i].b=b;qq[i].w=w;
}
sort(v.begin(),v.end()); //从小到大处理点
for(int i=0;i<v.size();i++)
{
getf(v[i]);
}
for(int i=0;i<n;i++)
{
int t1=getv(qq[i].a);
int t2=getv(qq[i].b);
adde(t1,t2,qq[i].w,1); //注意点:若使用adde(getv(a),getv(b),w,1)参数是从右往左开始赋值传递的!!!
}
for(int i=1;i<countv;i++)
{
adde(i+1,i,0,inf); //注意点:若使用adde(getv(a),getv(b),w,1)参数是从右往左开始赋值传递的!!!
}
adde(0,1,0,inf);adde(countv,countv+1,0,k);
int sumcost=0; int sumflow=0;
mincost(sumcost,sumflow);
cout<<"Case "<<ii<<": ";
if(sumflow!=k) //到不了k,无解
{
cout<<-1<<endl;
}
else
{
cout<<sumcost<<endl;
}
}
return 0;
}
2014湘潭全国邀请赛I题 Intervals /POJ 3680 / 在限制次数下取有权区间使权最大/小问题(费用流)的更多相关文章
- 2014上海全国邀请赛题解 HDOJ 5090-5099
HDOJ 5090 水题.从小到大排序,能够填充达到符合条件的.先填充好.填充之后进行调整. 传送门:pid=5090">点击打开链接 #include <cstdio> ...
- HDU 4597 Play Game 2013 ACM-ICPC吉林通化全国邀请赛H题
九野的博客,转载请注明出处: http://blog.csdn.net/acmmmm/article/details/10833941 题意:给定T个测试数据,下面有2副牌,每副n张,每张都有一个分 ...
- 湘潭oj1203/邀请赛A题 数论+java大数
求 n%1+n%2+n%3+n%4+.........n%n=,n<=10^12次. 开始时盲目地找规律,结果一无所获.后来经学长点拨,天资愚钝,搞了半天才明白. 先上图: 对于该题,在求区间( ...
- hdu - 6281,2018CCPC湖南全国邀请赛F题,快排
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6281 题意: 根据已给出的式子,进行排序,然后输出排完序后原先的下表. 题解:用结构体保存,在用结构体 ...
- hdu - 6277,2018CCPC湖南全国邀请赛B题,找规律,贪心找最优.
题意: 给出N个小时,分配这些小时去写若干份论文,若用1小时写一份论文,该论文会被引用A次,新写一篇论文的话,全面的论文会被新论文引用一次. 找最大的H,H是指存在H遍论文,而且这些论文各被引用大于H ...
- hdu - 6276,2018CCPC湖南全国邀请赛A题,水题,二分
题意: 求H的最大值, H是指存在H篇论文,这H篇被引用的次数都大于等于H次. 思路:题意得, 最多只有N遍论文,所以H的最大值为N, 常识得知H的最小值为0. 所以H的答案在[0,N]之间,二分 ...
- hdu - 6282,2018CCPC湖南全国邀请赛G题,字符串,规律
HDU – 6282 http://acm.hdu.edu.cn/showproblem.php?pid=6282 by Hzu_Tested 题意:给出两个字符串S和T,只由a,b,c三种字符组成( ...
- Intervals POJ - 3680 (MCMF)
给你一些区间,每个区间都有些价值.取一个区间就能获得对应的价值,并且一个点不能覆盖超过k次,问你最大的价值是多少. 我们可以把这些区间放到一维的轴上去,然后我们可以把它看成一个需要从左到右的过程,然后 ...
- Intervals POJ - 3680
传送门 给定数轴上n个带权区间$[l_i,r_i]$,权值为$w_i$ 选出一些区间使权值和最大,且每个点被覆盖次数不超过k次. 离散+拆点,最大费用可行流(跑到费用为负为止) 第一部分点按下标串起来 ...
随机推荐
- xheditor的实例程序—类似word的编辑器
编辑器工具栏:类似word的编辑器 1.1.下载,兼容性 xhEditor官方网站地址为:http://xheditor.com/,打开右上角的免费下载 | 参数向导链接,即可找到最新版本的下载地址. ...
- Luogu P3938 斐波那契
Luogu P3938 斐波那契 第一眼看到这题,想到的是LCA,于是开始想怎么建树,倒是想出了\(n^{2}\)算法,看了下数据范围,果断放弃 想了想这数据范围,大的有点不正常,这让我想起了当年被小 ...
- 实体类和JSON对象之间相互转化
. [代码]工具类 ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 3 ...
- Wash!!(HDU_6000)
传送门:Wash! 题意:有n台洗衣机,m台烘干机,给出了每台机器处理意见衣服的时间,而且没见机器同时只能处理一件衣服.问如何选择机器才能使洗完衣服的时间最短. 思路:建两个优先队列,一个表示洗衣机, ...
- mysql8忘记root密码修改密码(mac)
0.在/etc/my.cnf修改验证方式 [mysqld] default_authentication_plugin=mysql_native_password 1.切换root权限: sudo s ...
- perl学习之子程序
一.定义子程序即执行一个特殊任务的一段分离的代码,它可以使减少重复代码且使程序易读.PERL中,子程序可以出现在程序的任何地方.定义方法为:sub subroutine{statements;}二.调 ...
- js/jquery判断浏览器 & 停止加载
JS获取浏览器信息 复制代码代码如下: 浏览器代码名称:navigator.appCodeName浏览器名称:navigator.appName浏览器版本号:navigator.appVersion对 ...
- 转:深入 AngularUI Router
原文地址:http://www.ng-newsletter.com/posts/angular-ui-router.html ui-router: https://angular-ui.github. ...
- [转]python开发_shelve_完整版
''' python中的shelve模块,可以提供一些简单的数据操作 他和python中的dbm很相似. 区别如下: 都是以键值对的形式保存数据,不过在shelve模块中, key必须为字符串,而值可 ...
- 公钥密码之RSA密码算法大素数判定:Miller-Rabin判定法!
公钥密码之RSA密码算法大素数判定:Miller-Rabin判定法! 先存档再说,以后实验报告还得打印上交. Miller-Rabin大素数判定对于学算法的人来讲不是什么难事,主要了解其原理. 先来灌 ...