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次. 离散+拆点,最大费用可行流(跑到费用为负为止) 第一部分点按下标串起来 ...
随机推荐
- ubuntu frp 自编译。本文不能按顺序来 请自己理解
go run:go run 编译并直接运行程序,它会产生一个临时文件(但不会生成 .exe 文件),直接在命令行输出程序执行结果,方便用户调试. go build:go build 用于测试编译包,主 ...
- 前台解析json的方法
JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式,采用完全独立于语言的文本格式,是理想的数据交换格式.同时,JSON是 JavaScript 原生格式,这意 ...
- bootstrap历练实例:复选框或单选按钮作为输入框组的前缀或后缀
<!DOCTYPE html><html><head><meta http-equiv="Content-Type" content=&q ...
- https 调用验证失败 peer not authenticated
https 调用验证失败 peer not authenticated 报错日志: Caused by: javax.net.ssl.SSLPeerUnverifiedException: peer ...
- KVM 重命名虚机
KVM 重命名虚机 1. 查看虚机列表 [root@bjape01-kvm1 ~]# virsh list --all Id 名称 状态 --- ...
- 初涉最小表示法&&bzoj1398: Vijos1382寻找主人 Necklace
把最小表示法的坑填了 Description 给定两个项链的表示,判断他们是否可能是一条项链. Input 输入文件只有两行,每行一个由0至9组成的字符串,描述一个项链的表示(保证项链的长度是相等的) ...
- 简单几点让你快速了解python是什么
1.python是什么 python是一种广泛使用的高级编程语言,属于通用型编程语言,由吉多·范罗苏姆创造,第一版发布于1991年.可以视之为一种改良(加入一些其他编程语言的优点,如面向对象)的LIS ...
- wordcloud的安装报错 error: Microsoft Visual C++ 10.0 is required. Get it with "Microsoft Windows SDK 7.1"解决办法
cmd中使用pip install wordcloud失败,没看懂报错的原因…… 而在pycharm中添加也报错 解决方法: 1. 下载wordcloud-1.4.1.tar.gz,解压缩 cmd c ...
- errno的定义
./include/asm-generic/errno-base.h -->包含errno=~ ./arch/arm/include/asm/errno.h -->包含/include/a ...
- PAT Basic 1017
1017 A除以B(20 分) 本题要求计算 A/B,其中 A 是不超过 1000 位的正整数,B 是 1 位正整数.你需要输出商数 Q 和余数 R,使得 A=B×Q+R 成立. 输入格式: 输入在一 ...