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次. 离散+拆点,最大费用可行流(跑到费用为负为止) 第一部分点按下标串起来 ...
随机推荐
- OpenWrite方法打开现有文件并进行写入
实现效果: 知识运用: File类的OpenWrite方法 //实现打开现有文件以进行写入 public static FileStream OpenWrite (string path) Encod ...
- python常用模块之requests
一.requests 1.GET url带参数请求 >>> payload = {'key1': 'value1', 'key2': 'value2'} >>> ...
- Codeforces Round #273 (Div. 2)-C. Table Decorations
http://codeforces.com/contest/478/problem/C C. Table Decorations time limit per test 1 second memory ...
- javaEE(12)_数据库连接池
一.直接获取数据库连接和通过池获取示意图: 二.编写数据库连接池 1.实现DataSource接口,并实现连接池功能的步骤: •在DataSource构造函数中批量创建与数据库的连接,并把创建的连接加 ...
- ios之UITabelViewCell的自定义(代码实现)
在用到UITableVIew的时候,经常会自定义每行的Cell 在IOS控件UITableView详解中的下面代码修改部分代码就可以实现自定义的Cell了 [cpp] view plaincopy - ...
- Django项目部署:使用uwsgi和nginx的方式
一.背景 前两天制作的个人博客网站基本完工,大致功能具备.但是在部署环节却也处处碰壁,这里也来总结以下,以备将来不时查看以及完善. 二.前提 2.1 需要的知识 django Django是一个基于p ...
- 目录扫描工具DirBuster
DirBuster是用来探测web服务器上的目录和隐藏文件的.因为DirBuster是采用java编写的,所以运行前要安装上java的环境. 来看一下基本的使用: ①:TargetURL下输入要探测网 ...
- docker系列之网络配置
docker 网络配置 docker 安装后, 会自动在系统做一个网桥配置 docker0 . 其容器都会分配到此网桥配置下的独立, 私有 IP 地址. 如果你要自己配置桥接, 也可以把 docker ...
- Python9-day4 作业
#!/usr/bin/env python# -*- coding:utf-8 -*-# Author:Timli = ["alex", "eric", &qu ...
- LeetCode(119) Pascal's Triangle II
题目 Given an index k, return the kth row of the Pascal's triangle. For example, given k = 3, Return [ ...