配置魔药 [NOIP模拟] [DP] [费用流]
问题描述
在《Harry Potter and the Chamber of Secrets》中,Ron 的魔杖因为坐他老爸的 Flying Car 撞到了打人柳,不幸被打断了,从此之后,他的魔杖的魔力就大大减少,甚至没办法执行他施的魔咒,这为 Ron 带来了不少的烦恼。这天上魔药课,Snape 要他们每人配置一种魔药(不一定是一样的),Ron 因为魔杖的问题,不能完成这个任务,他请 Harry 在魔药课上(自然是躲过了 Snape 的检查)帮他配置。现在 Harry 面前有两个坩埚,有许多种药材要放进坩埚里,但坩埚的能力有限,无法同时配置所有的药材。一个坩埚相同时间内只能加工一种药材,但是不一定每一种药材都要加进坩埚里。加工每种药材都有必须在一个起始时间和结束时间内完成(起始时间所在的那一刻和结束时间所在的那一刻也算在完成时间内),每种药材都有一个加工后的药效。现在要求的就是 Harry 可以得到最大的药效。
输入
输入文件的第一行有 2 个整数,一节魔药课的 t(1≤t<≤500)和药材数 n(1≤n≤100)。
输入文件第 2 行到 n+1 行中每行有 3 个数字,分别为加工第 i 种药材的起始时间 t1、结束时间 t2、(1≤t1≤t2≤t)和药效 w(1≤w≤100)。
输出
输出文件 medic.out 只有一行,只输出一个正整数,即为最大药效。
输入样例
7 4
1 2 10
4 7 20
1 3 2
3 7 3
输出样例
35
注释
本题的样例是这样实现的:第一个坩埚放第 1、4 种药材,第二个坩埚放第
2、3 种药材。这样最大的药效就为 10+20+2+3=35。
如图,数字为时间轴。
数据规模
对于 30%的数据
1<=t<=500
1<=n<=15
1<=w<=100
1<=t1<=t2<=t
对于 100%的数据
1<=t<=500
1<=n<=100
1<=w<=100
1<=t1<=t2<=t
分析
这道题我和std写的不太一样,std用的是DP,而我太弱了,只会写一个费用流...
一、STD的DP
看完题目后,对题目分析可知,此题的最优解法就是动态规划。[???]
当然,因为有两个坩锅,所以明显是一道双进程动态规划题目。因此,先用动态规划求解一个坩锅能达到的最大药效,把已用的药材去掉,再次动态规划的方法是不可行的!
1.分析最优子结构:
数据备注:
t1[i]表示第i种药材的起始时间
t2[i]表示第i种药材的结束时间
w[i]表示第i种药材的药效
time表示总时间 n表示药材总数
(为了让大家更好理解 先讲一种未优化的算法)
设动态规划数组 dp[i][j][k] 表示前i种药材放入两个坩锅,第一个坩锅达到时间j,第二个坩锅达到k时所能达到的最大药效。题目的解就是dp[n][time][time];
因为要知道在time时刻两个坩锅的效益和,则需要用到它的子结构的最优值。
分析子结构的最优值的取得条件:
对于第i种药材,放入两个坩锅,有三种处理方法:
1. 把它放入第一个坩锅;
2. 把它放入第二个坩锅;
3. 不放入任何坩锅
对1:
dp[i][j][k]=dp[i-1][t1[i]-1][k]+w[i]; 条件:仅当j=t2[i];
对2:
dp[i][j][k]=dp[i-1][j][t1[i]-1]+w[i]; 条件:仅当k=t2[i];
对3:
dp[i][j][k]=max{ dp[i-1][j][k], 无条件
dp[i][j-1][k], 条件 j>0
dp[i][j][k-1], 条件 k>0
因此 子结构dp[i][j][k]的取值就是对1,2,3种状态的最大值
即:
dp[i][j][k]=max{
dp[i-1][t1[i]-1][k]+w[i]; 条件:仅当j=t2[i];
dp[i-1][j][t1[i]-1]+w[i]; 条件:仅当k=t2[i];
dp[i-1][j][k], 无条件
dp[i][j-1][k], 条件 j>0
dp[i][j][k-1], 条件 k>0
}
2.解决后效性:
为了解决后效性,在这里,当且仅当j=t1[i]或k=t1[i]时,药材才会被放入坩锅内,且保证了同一种药品不会在以后被多次放入或者是同时放入二个坩锅,当然,仅仅是这样还是不能保证能求出最优解,因为在计算过程中,结构会被刷新,因此对于结束时间较晚的药材,若在结束时间较前的药材先被计算,则较前的药材就以为价值小而不会被记录,因此就应该在动态规划之前将数据按结束时间t2[i]升序排序。因此,我们完美的消除了后效性。
3.优化问题:
1.时间复杂度的优化:
根据对称性,两个坩锅不计先后,且一摸一样,因此dp[i][j][k]等价于dp[i][k][j],因此,在循环的时候可以令j<=k;然后加几个判断即可。具体方法不再赘述,请自行解决。
2.空间复杂度的优化:
在求解的过程中 即:dp[i][j][k]的值 只与 dp[i-1][…][…]的值有关,因此可以将数组降为2维 即:dp[j][k],具体方法不再赘述。可以看标程,标程有两个,一个是优化过的,一个是未经过优化的。
因此,对于这道题目,
近似最优时间复杂度为O(n^3)
近似最优空间复杂度为O(n^2)
#include<stdio.h>
int dp[][]={};
int t1[]={},t2[]={},w[]={},t,n; void quick_sort(int l,int r)//按t2升序排序
{int i,k,a;
if(l>=r)
return;
k=l-;
for(i=l;i<r;i++)
if(t2[i]<t2[r])
{k++;
a=t2[k];
t2[k]=t2[i];
t2[i]=a;
a=t1[k];
t1[k]=t1[i];
t1[i]=a;
a=w[k];
w[k]=w[i];
w[i]=a;
}
a=t2[k+];
t2[k+]=t2[r];
t2[r]=a;
a=t1[k+];
t1[k+]=t1[r];
t1[r]=a;
a=w[k+];
w[k+]=w[r];
w[r]=a;
quick_sort(l,k);
quick_sort(k+,r);
} FILE *fin,*fout;
main()
{fin=fopen("medic.in","r");
fout=fopen("std.out","w");
int i,j,k,q,a;
fscanf(fin,"%d%d",&t,&n);
for(i=;i<=n;i++)
fscanf(fin,"%d%d%d",&t1[i],&t2[i],&w[i]);
quick_sort(,n);
for(i=;i<=n;i++)//动态规划
for(j=t;j>=;j--)
for(k=t;k>=;k--)
{if(j>=t2[i])
if(dp[j][k]<dp[t1[i]-][k]+w[i])
dp[j][k]=dp[t1[i]-][k]+w[i];
if(k>=t2[i])
if(dp[j][k]<dp[j][t1[i]-]+w[i])
dp[j][k]=dp[j][t1[i]-]+w[i];
}
fprintf(fout,"%d",dp[t][t]);
fclose(fin);
fclose(fout);
}
二、我写的费用流我看到这道题,嘴上笑嘻嘻,心里mmp,看起来又是一道DP/贪心题?
我最讨厌的就是这个东西了
于是联想以前的题目想到了USACO Nov07挤奶时间 那到题,是个单线程的求最大收益值,于是我就u->v连了条权值为收益的边,跑遍最长路
这道题不就是把单线程改成了双线程吗?两次最长路是显然错误的,因为不一定是最优。但他在跑完之后能反悔就好了
反悔?我们想到了网络流的反向边,于是我们尝试把这道题转化成一个网络流的模型
u->v+1连一条费用为收益,流量为1的边(v+1是因为包括了末节点的时间),然后从起点到终点,连一条i->i+1,费用为0,流量为2的边
这样限制了进程数为2,同时跑一边最大费用最大流,就AC了 (然后这样的算法完爆STD?测试我的0s ,std 0.7s)
#include<set>
#include<map>
#include<stack>
#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define RG register int
#define rep(i,a,b) for(RG i=a;i<=b;i++)
#define per(i,a,b) for(RG i=a;i>=b;i--)
#define ll long long
#define inf (1<<30)
#define maxm 5005
using namespace std;
int n,m,S,T,cnt=;
int head[maxm],dis[maxm],step[maxm],vis[maxm];
struct E{
int u,v,fl,cost,next;
}e[maxm];
inline int read()
{
int x=,f=;char c=getchar();
while(c<''||c>''){if(c=='-')f=-;c=getchar();}
while(c>=''&&c<=''){x=x*+c-'';c=getchar();}
return x*f;
} inline void add(int u,int v,int fl,int cost)
{
e[++cnt].u=u,e[cnt].v=v,e[cnt].fl=fl,e[cnt].cost=cost,e[cnt].next=head[u],head[u]=cnt;swap(u,v);
e[++cnt].u=u,e[cnt].v=v,e[cnt].fl=,e[cnt].cost=-cost,e[cnt].next=head[u],head[u]=cnt;
} bool SPFA()
{
memset(dis,,sizeof(dis));
memset(step,-,sizeof(step));
memset(vis,,sizeof(vis));
queue<int> que;que.push(S),dis[S]=;RG u,v;
while(!que.empty())
{
u=que.front(),que.pop();vis[u]=;
for(RG i=head[u];i;i=e[i].next)
{
v=e[i].v;
if(e[i].fl&&dis[v]>dis[u]+e[i].cost)
{
dis[v]=dis[u]+e[i].cost;
step[v]=i;
if(!vis[v]) vis[v]=,que.push(v);
}
}
}
return (step[T]!=-);
} void MCMF()
{
int ans=;
while(SPFA())
{
int fl=inf;
for(RG i=step[T];i!=-;i=step[e[i].u]) fl=min(fl,e[i].fl);
for(RG i=step[T];i!=-;i=step[e[i].u]) e[i].fl-=fl,e[i^].fl+=fl,cout<<e[i].v<<" ";
ans+=dis[T];
}
cout<<-ans;
} int main()
{
freopen("medic.in","r",stdin);
freopen("medic.out","w",stdout);
n=read(),m=read();S=,T=n+;
for(RG i=,u,v,cost;i<=m;i++) u=read(),v=read(),cost=read(),add(u,v+,,-cost);
rep(i,,n) add(i-,i,,);
add(S,,,);add(n,T,,);
MCMF();
return ;
}
配置魔药 [NOIP模拟] [DP] [费用流]的更多相关文章
- NOIP 模拟 box - 费用流 / 匈牙利
题目大意: 给出n(\(\le 200\))个盒子,第i个盒子长\(x_i\),宽\(y_i\),一个盒子可以放入长宽都大于等于它的盒子里,并且每个盒子里只能放入一个盒子(可以嵌套),嵌套的盒子的占地 ...
- 2019.6.1 模拟赛——[ 费用流 ][ 数位DP ][ 计算几何 ]
第一题:http://codeforces.com/contest/1061/problem/E 把点集分成不相交的,然后跑费用流即可.然而错了一个点. #include<cstdio> ...
- [LOJ2288][THUWC2017]大葱的神力:搜索+背包DP+费用流+随机化
分析 测试点1.2:搜索+剪枝. 测试点3:只有一个抽屉,直接01背包. 测试点4.5:每个物品体积相同,说明每个抽屉能放下的物品个数固定,建图跑费用流. 测试点6:每个物品体积相近,经过验证发现每个 ...
- AIM Tech Round 3 (Div. 1) (构造,树形dp,费用流,概率dp)
B. Recover the String 大意: 求构造01字符串使得子序列00,01,10,11的个数恰好为$a_{00},a_{01},a_{10},a_{11}$ 挺简单的构造, 注意到可以通 ...
- 2018.08.19 NOIP模拟 dp(二分+状压dp)
Dp 题目背景 SOURCE:NOIP2015-SHY-10 题目描述 一块土地有 n 个连续的部分,用 H[1],H[2],-,H[n] 表示每个部分的最初高度.有 n 种泥土可用,他们都能覆盖连续 ...
- BZOJ4946[Noi2017]蔬菜——线段树+堆+模拟费用流
题目链接: [Noi2017]蔬菜 题目大意:有$n$种蔬菜,每种蔬菜有$c_{i}$个,每种蔬菜每天有$x_{i}$个单位会坏掉(准确来说每天每种蔬菜坏掉的量是$x_{i}-$当天这种蔬菜卖出量), ...
- 【BZOJ3252】攻略 DFS序+线段树(模拟费用流)
[BZOJ3252]攻略 Description 题目简述:树版[k取方格数] 众所周知,桂木桂马是攻略之神,开启攻略之神模式后,他可以同时攻略k部游戏. 今天他得到了一款新游戏<XX半岛> ...
- CF280D-k-Maximum Subsequence Sum【模拟费用流,线段树】
正题 题目链接:https://www.luogu.com.cn/problem/CF280D 题目大意 一个长度为\(n\)的序列,\(m\)次操作 修改一个数 询问一个区间中选出\(k\)段不交子 ...
- BZOJ4849[Neerc2016]Mole Tunnels——模拟费用流+树形DP
题目描述 鼹鼠们在底下开凿了n个洞,由n-1条隧道连接,对于任意的i>1,第i个洞都会和第i/2(取下整)个洞间有一条隧 道,第i个洞内还有ci个食物能供最多ci只鼹鼠吃.一共有m只鼹鼠,第i只 ...
随机推荐
- JAVA 程序编译过程;编辑器,编译器和解释器
最基本的软件工具包括,编辑器,编译器,解释器; 编译器:编译器就是将一种编程语言代码翻译成另一种语言的等效代码程序. 解释器:解释器将编译和执行交织在一起,即编译一部分代码后执行该部分代码,然后再编译 ...
- azkaban group分组,权限
翻译自:https://azkaban.readthedocs.io/en/latest/userManager.html?highlight=group 1.job project,名为" ...
- ssh-keygen -t rsa 生成密钥对后如何校验
ssh-keygen -t rsa 生成密钥对后如何校验一下呢ssh-keygen -y -f id_rsa > id_rsa.pub.tobecompared 然后对比一下id_rsa.pub ...
- .Net Core下发送WebRequest请求的两种方式
1.使用RestSharp.NetCore 2.使用WebApi请求方式
- jquery源码中noConflict(防止$和jQuery的命名冲突)的实现原理
jquery源码中noConflict(防止$和jQuery的命名冲突)的实现原理 最近在看jquery源码分析的视频教学,希望将视频中学到的知识用博客记录下来,更希望对有同样对jquery源码有困惑 ...
- bzoj 2832
题解: 首先有一个比较显然的事情是如果我们确定了买的次数这道题就可以简单的贪心了 但是答案和买的次数是什么关系呢.. 好像是可以三分的 所以应该是单峰的 这里用了模拟退火,而且是没有处理失败情况的模拟 ...
- 【BZOJ2402】陶陶的难题II 分数规划+树链剖分+线段树+凸包
题解: 首先分数规划是很明显的 然后在于我们如何要快速要求yi-mid*xi的最值 这个是看了题解之后才知道的 这个是斜率的一个基本方法 我们设y=mid*x+z 那么显然我们可以把(x,y)插入到一 ...
- 关于LookUp的切换实例
在关注了很久后发现还是有太多的东西没有记住,需要自己一步一步地去学习!不用太关注太多的杂乱的事,只用关注自己一点一滴,我相信所有的一切都会有一个很好的结局.
- mongodb中投票节点作用
投票节点 并不含有 复制集中的数据集副本,且也 无法 升职为主节点.投票节点的存在是为了使复制集中的节点数量为奇数,这样保证在进行投票的时候不会出现票数相同的情况.如果添加了一个节点后,总节点数为偶数 ...
- c#生成连续单号
bill.BillCode = GetBillCode("JH");//生成单号 if (bill.BillCode == "no") { bill.BillC ...