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 / 在限制次数下取有权区间使权最大/小问题(费用流)

时间: 2024-09-30 14:49:25

2014湘潭全国邀请赛I题 Intervals /POJ 3680 / 在限制次数下取有权区间使权最大/小问题(费用流)的相关文章

湘潭oj1203/邀请赛A题 数论+java大数

求 n%1+n%2+n%3+n%4+.........n%n=,n<=10^12次. 开始时盲目地找规律,结果一无所获.后来经学长点拨,天资愚钝,搞了半天才明白. 先上图: 对于该题,在求区间(根号n,n),由于n%i=n-i*x(这里x是从1枚举到根号n,每个k 对应n/(x+1)~n/x区间内,由于是等差数列(还是递减),直接用公式求和). 哎(根号n,n)区间是被分割来求得,分成根号n次. import java.io.*; import java.util.Scanner; import

2014上海全国邀请赛题解 HDOJ 5090-5099

HDOJ 5090 水题,从小到大排序,可以填充达到符合条件的,先填充好,填充之后进行调整. 传送门:点击打开链接 #include <cstdio> #include <cmath> #include <queue> #include <map> #include <vector> #include <cstring> #include <algorithm> using namespace std; const int

POJ 2761-Feed the dogs(划分树)求区间内第k小的数

Feed the dogs Time Limit: 6000MS   Memory Limit: 65536K Total Submissions: 17679   Accepted: 5561 Description Wind loves pretty dogs very much, and she has n pet dogs. So Jiajia has to feed the dogs every day for Wind. Jiajia loves Wind, but not the

2014上海全国邀请赛——题目重现 4.24

连接:http://acm.hdu.edu.cn/search.php?field=problem&key=2014%C9%CF%BA%A3%C8%AB%B9%FA%D1%FB%C7%EB%C8%FC%A1%AA%A1%AA%CC%E2%C4%BF%D6%D8%CF%D6%A3%A8%B8%D0%D0%BB%C9%CF%BA%A3%B4%F3%D1%A7%CC%E1%B9%A9%CC%E2%C4%BF%A3%A9&source=1&searchmode=source 只出了3个题

2014西安全国邀请赛

D - Wow! Such String!: 这道字符串题居然正解是欧拉图,我也是看醉了...欧拉图虽然学过,但可能理解得还不是很深,所以对这种题不敏感. 这道题解法是把每四个字符组成的字符串看成是一个点,每个点有26个入边和26个出边,可以证明这是一个欧拉图.然后遍历找一下欧拉图就行了. 不过...一开始我是自己建了个图...结果超内存...然后想打表...结果根本不让我交...实在无奈,改成了每个点自己找下一个点,不连边.(现在想想连边好像是没有必要的)另,看题的时候还是要仔细看题目要求,尽

2014上海全国邀请赛 解题报告

Game with Pearls 贪心水题 http://blog.csdn.net/u012774187/article/details/40711559 Beam Cannon 线段树陈题 http://blog.csdn.net/u012774187/article/details/40712359 Seam Carving 简单DP http://blog.csdn.net/u012774187/article/details/40712899 Battle ships 二分图陈题 ht

[voj 1551]E - Pairs 2014年武汉大学邀请赛E题 莫队算法

题目大意 有n个数,m个查询,对于每个查询,询问指定区间,有多少个数对的绝对值小于等于2. 解题思路 莫队O^1.5 首先将询问离线处理左端点进行编号,每sqrt(n)个为一组 sort结构体 当左端点编号相同时,比较右端点大小.小的放在前面. 对于每组询问暴力处理,只需处理当前新加入(删除的数字在当前区间内有多少点和它的绝对值只差小于2即可) 唯一要注意的是加点是先更新答案再计数,删点是先计数器-1再更新答案 因为对于每个询问,左端点的修改量不超过sqrt(n) 右端点每一组最坏的复杂度是修改

hdu - 6276,2018CCPC湖南全国邀请赛A题,水题,二分

题意: 求H的最大值,  H是指存在H篇论文,这H篇被引用的次数都大于等于H次. 思路:题意得,  最多只有N遍论文,所以H的最大值为N, 常识得知H的最小值为0. 所以H的答案在[0,N]之间,二分搜一下,如果满足就提高下限,不满足则降低上限. 嗯就这样!!!! AC code: #include<bits/stdc++.h> using namespace std; int n; vector<int> cc(200005); int main() { bool check(i

HDOJ Page Rank 5097【2014上海邀请赛H题-简单矩阵】

Page Rank Time Limit: 3000/1500 MS (Java/Others)    Memory Limit: 100000/100000 K (Java/Others) Total Submission(s): 282    Accepted Submission(s): 77 Problem Description Evaluation and rank of web pages is a hot topic for many internet companies and