刷题总结——怪题(ssoj费用流)

题目:

题目描述

给出一个长度为 n 的整数序列 hi ,现在要通过一些操作将这个序列修改为单调不降序列,即  hi≤hi+1 。

可以用的操作有 m 种,第 i 种操作可以通过支付 ci 的代价将一段长度恰为 li 的连续子序列 +1 或 −1(由对应的操作符确定是 +1 还是 −1 ,具体参考输入格式)。

不限制每种操作的使用次数,序列中的 hi 可以被改为任意整数(可以是负数),求最小代价,无解输出 −1 。

输入格式

第一行,两个整数 n,m 。
第二行,n 个整数 hi 。
接下来 m 行,每行格式为 opi,li,ci ,空格隔开,其中 opi 为一个字符,表示这种操作是 +1 还是 −1 。

输出格式

输出一行一个整数表示最小代价,若无解输出 -1 。

样例数据 1

输入  [复制]

 

 

3 2 
3 2 1 
+ 1 1 
- 1 1

输出

2

样例数据 2

输入  [复制]

 

 

3 1 
3 2 1 
+ 2 1

输出

-1

样例数据 3

输入  [复制]

 

 

10 10 
23 1 8 14 2 3 15 50 53 53 
+ 4 6 
- 1 10 
+ 2 4 
+ 4 2 
- 3 5 
+ 1 2 
+ 3 2 
+ 5 7 
- 1 6 
+ 4 5

输出

96

备注

【数据规模】
对于 20% 的数据:n,m≤5;hi≤10;ci≤3 ;
对于另 20% 的数据:li=1;hi≤500 ;
对于 100% 的数据:n,m≤200;li≤n;1≤hi,ci≤106 。

题解:

  

其实这道题看数据范围外加分析是很容易猜出是费用流的·····难的是建图····

代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<cctype>
#include<cstring>
#include<string>
#include<algorithm>
#include<queue>
using namespace std;
const int N=205;
const int M=400005;
const int INF=0x3f3f3f3f;
queue<int>que;
int tot=1,first[N],next[M],go[M],rest[M],cost[M],S,T;
int n,m,h[N],Tot,totflow,dis[N];
long long ans=0;
bool visit[N],work[N];
char s[5];
inline int R()
{
  char c;int f=0,i=1;
  for(c=getchar();(c<'0'||c>'9')&&c!='-';c=getchar());
  if(c=='-')  c=getchar(),i=-1; 
  for(;c<='9'&&c>='0';c=getchar())
    f=(f<<3)+(f<<1)+c-'0';
  return f*i;
}
inline void comb(int a,int b,int v,int w)
{
  next[++tot]=first[a],first[a]=tot,go[tot]=b,rest[tot]=v,cost[tot]=w;
  next[++tot]=first[b],first[b]=tot,go[tot]=a,rest[tot]=0,cost[tot]=-w;
}
inline bool spfa()
{
  memset(dis,INF,sizeof(dis));memset(work,false,sizeof(work));dis[S]=0;
  que.push(S);
  while(!que.empty())
  {
    int u=que.front();que.pop();visit[u]=false;
    for(int e=first[u];e;e=next[e])
    {
      int v=go[e];
      if(dis[v]>dis[u]+cost[e]&&rest[e]>0)
      {
        dis[v]=dis[u]+cost[e];
        if(!visit[v])
        {
          visit[v]=true;que.push(v);
        }
      }
    }
  }
  return dis[T]!=INF;
}
inline int dinic(int u,int flow)
{
  if(u==T)
  {
    ans+=(long long)dis[T]*flow;
    return flow;
  }
  int v,delta,res=0;work[u]=true;
  for(int e=first[u];e;e=next[e])
  {
    if(dis[v=go[e]]==dis[u]+cost[e]&&rest[e]>0&&!work[v])
    {
      delta=dinic(v,min(flow-res,rest[e])); 
      if(delta)
      {
        res+=delta;rest[e]-=delta;rest[e^1]+=delta;
        if(res==flow)  break;
      }
    }
  }
  return res;
}

inline void maxflow()
{
  while(spfa())
    totflow+=dinic(S,INF);
}
int main()
{
  //freopen("a.in","r",stdin); 
  n=R(),m=R();S=0,T=n+2;
  for(int i=1;i<=n;i++) 
  {
    h[i]=R();
    if(i==1)  comb(S,i,INF,0);
    else
    {
      if(h[i]-h[i-1]>0)  comb(S,i,h[i]-h[i-1],0);
      else if(h[i]-h[i-1]<0)  comb(i,T,h[i-1]-h[i],0),Tot+=h[i-1]-h[i];
    }
  }
  comb(S,n+1,INF,0);int l,c;
  while(m--)
  {
    scanf("%s",s);l=R(),c=R();
    if(s[0]=='+')
      for(int i=n+1;i-l>=1;i--)  comb(i,i-l,INF,c);
    else
      for(int i=1;i+l<=n+1;i++)  comb(i,i+l,INF,c);
  }
  maxflow();
  if(totflow!=Tot)  cout<<"-1"<<endl;
  else cout<<ans<<endl;
  return 0;
}

 

转载于:https://www.cnblogs.com/AseanA/p/7638046.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值