您的位置:首页 > 其它

[DP] [1D1D优化] [树状数组] [最短路] 遭遇战 (clean)

2016-06-05 22:01 429 查看
背景 Background

你知道吗,SQ Class的人都很喜欢打CS。(不知道CS是什么的人不用参加这次比赛)。

题目描述 Description

今天,他们在打一张叫DUSTII的地图,万恶的恐怖分子要炸掉藏在A区的SQC论坛服务器!我们SQC的人誓死不屈,即将于恐怖分子展开激战,准备让一个人守着A区,这样恐怖分子就不能炸掉服务器了。(一个人就能守住??这人是机械战警还是霹雳游侠?)

但是问题随之出现了,由于DustII中风景秀丽,而且不收门票,所以n名反恐精英们很喜欢在这里散步,喝茶。他们不愿意去单独守在荒无人烟的A区,在指挥官的一再命令下,他们终于妥协了,但是他们每个人都要求能继续旅游,于是给出了自己的空闲时间,而且你强大的情报系统告诉了你恐怖份子计划的进攻时间(从s时刻到e时刻)。

当然,精明的SQC成员不会为你免费服务,他们还要收取一定的佣金(注意,只要你聘用这个队员,不论他的执勤时间多少,都要付所有被要求的佣金)。身为指挥官的你,看看口袋里不多的资金(上头真抠!),需要安排一个计划,雇佣一些队员,让他们在保证在进攻时间里每时每刻都有人员执勤,花费的最少资金。

输入 Input

第一行是三个整数n(1≤n≤10000),s和e(1≤s≤e≤90000)。

接下来n行,描述每个反恐队员的信息:空闲的时间si,ei(1≤si≤ei≤90000)和佣金ci(1≤ci≤300000)。

输出 Output

一个整数,最少需支付的佣金,如果无解,输出“−1”。

样例输入 Sample Input

3 1 5

1 3 3

4 5 2

1 1 1

样例输出 Sample Output

5

提示 Hints

敌人从1时刻到4时刻要来进攻,一共有3名反恐队员。第1名从1时刻到3时刻有空,要3元钱(买糖都不够??)。以此类推。

一共要付5元钱,选用第1名和第2名。

限制 Limits

数据范围见题目

Time Limit : 1s & Memory Limit : 128MB

首先我要吐槽一下题目解释,根本物理没学好好吗?时间是区间而时刻是点好吗?解释一下是打错了还是怎么回事(物理老师闪现)

看题,明显的带权值线段覆盖,DP可以解决权值为1的线段覆盖问题。

思路就是枚举a起点,在a起点的终点之前找到与之覆盖的b起点,b起点覆盖的最小代价已经求出,加上a线段的权值就可以了

那么就有一个严重的问题

求最小值枚举就是个T的,在来个T?T2?可以T了

那么就搞个什么东西维护一下这个DP数组

搞定区间最小值直接上树状数组,O(n+Tlog2T)的时间非常好

上代码

Code

(UPD:这好像是我写的第一个1D1D……)

但是……

还好吧,考场上绝对想不出来

下面就是一个非常优(tou)秀(ji)的方法:转化成图论题

时间区间可以看成从开始到结束的一条边,权值就是边权

为了使图联通,可以反向加边,即i+1时刻到i时刻有一条边权为0的边

上代码

#include <queue>
#include <cstdio>
#include <cstring>
#include <climits>
#define MAXN 100100
#define MAXT 900100
using namespace std;

struct link
{
int to;
int val;
int nxt;
};

link e[MAXN+MAXT];
int e_num[MAXN],cnt;
int n,s,t;
int start,finish;
int u,v,w;
int dis[MAXT],vis[MAXT];
queue <int> q;

int mymax(int a,int b)
{
return a>b?a:b;
}

int mymin(int a,int b)
{
return a<b?a:b;
}

void add(int u,int v,int w)
{
e[cnt]=(link){v,w,e_num[u]}; e_num[u]=cnt++;
}

int spfa()
{
dis[s]=0;vis[s]=true;q.push(s);
while (!q.empty())
{
int u=q.front();q.pop();vis[u]=false;
for (int i=e_num[u];i!=-1;i=e[i].nxt)
{
if (dis[e[i].to]>dis[u]+e[i].val)
{
dis[e[i].to]=dis[u]+e[i].val;
if (!vis[e[i].to])
{
vis[e[i].to]=true;
q.push(e[i].to);
}
}
}
}
if (dis[t]==INT_MAX) return -1;
else return dis[t+1];
}

int main()
{
memset(vis,false,sizeof(vis));
memset(e_num,-1,sizeof(e_num));
scanf("%d %d %d",&n,&s,&t);
for (int i=1;i<=n;i++)
{
scanf("%d %d %d",&u,&v,&w);
add(mymax(u,s),mymin(v,t)+1,w);
}
for (int i=s;i<=t+1;i++)
{
add(i+1,i,0);
dis[i]=INT_MAX;
}
printf("%d",spfa());
return 0;
}


据说正解是堆优化?

下面是标程(pascal党的福音)

const maxn=20000;
maxt=100000;
type re=record
data,pos:int64;
end;
var e,e2,i,n,t,t2,heapsize:longint;
a:array [0..maxn,1..3] of int64;
f:array [0..maxt] of int64;
heap:array [0..maxn] of re;
procedure change(var a,b:re);
var temp:re;
begin
temp:=a;
a:=b;
b:=temp;
end;
procedure insert(pos,data:int64);
var now,next:longint;
begin
inc(heapsize);
heap[heapsize].pos:=pos;
heap[heapsize].data:=data;
now:=heapsize;
while now<>1 do
begin
next:=now div 2;
if heap[now].data>=heap[next].data then exit;
change(heap[now],heap[next]);
now:=next;
end;
end;
procedure delete;
var now,next:longint;
begin
heap[1]:=heap[heapsize];
heap[heapsize].data:=0;
heap[heapsize].pos:=0;
dec(heapsize);
now:=1;
while now*2<=heapsize do
begin
next:=now*2;
if (next<heapsize)and(heap[next+1].data<heap[next].data) then inc(next);
if heap[now].data<=heap[next].data then exit;
change(heap[now],heap[next]);
now:=next;
end;
end;
procedure work;
var i:longint;
begin
heapsize:=1;
heap[1].data:=0;
heap[1].pos:=0;
fillchar(f,sizeof(f),0);
for i:=1 to n do
begin
while (heapsize>0)and(heap[1].pos<a[i,1]-1) do delete;
if heapsize=0 then
begin
writeln(-1);
exit;
end;
if (heap[1].data+a[i,3]<f[a[i,2]])or(f[a[i,2]]=0) then
begin
f[a[i,2]]:=heap[1].data+a[i,3];
insert(a[i,2],heap[1].data+a[i,3]);
end;
end;
if f[t]=0 then writeln(-1)
else writeln(f[t]);
end;
procedure sor(x,y:longint);
var i,k:longint;
begin
for i:=1 to 3 do
begin
k:=a[x,i];
a[x,i]:=a[y,i];
a[y,i]:=k;
end;
end;
procedure sort(l,r:longint);
var i,j:longint;
begin
i:=l;
j:=r;
while i<j do
begin
while (i<j)and((a[i,1]<a[j,1])or(a[i,1]=a[j,1])and(a[i,2]<=a[j,2])) do j:=j-1;
sor(i,j);
while (i<j)and((a[i,1]<a[j,1])or(a[i,1]=a[j,1])and(a[i,2]<=a[j,2])) do i:=i+1;
sor(i,j);
end;
if l<i-1 then sort(l,i-1);
if j+1<r then sort(j+1,r);
end;
begin
readln(n,e2,t2);
e:=e2-1;
t:=t2-e;
for i:=1 to n do
begin
readln(a[i,1],a[i,2],a[i,3]);
if a[i,1]<e2 then a[i,1]:=e2;
if a[i,2]>t2 then a[i,2]:=t2;
a[i,1]:=a[i,1]-e;
a[i,2]:=a[i,2]-e;
end;
sort(1,n);
work;
end.


DP里优(xia)化(gao)真神奇……
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: