最短路 + 动规
[ZJOI2006]物流运输
题目
题目传送门:luogu1772
题目描述
物流公司要把一批货物从码头A运到码头B。由于货物量比较大,需要n天才能运完。货物运输过程中一般要转停好几个码头。物流公司通常会设计一条固定的运输路线,以便对整个运输过程实施严格的管理和跟踪。由于各种因素的存在,有的时候某个码头会无法装卸货物。这时候就必须修改运输路线,让货物能够按时到达目的地。但是修改路线是—件十分麻烦的事情,会带来额外的成本。因此物流公司希望能够订一个n天的运输计划,使得总成本尽可能地小。
输入格式
第一行是四个整数n(l≤n≤100)、m(l≤m≤20)、K和e。n表示货物运输所需天数,m表示码头总数,K表示每次修改运输路线所需成本,e表示航线条数。接下来e行每行是一条航线描述,包括了三个整数,依次表示航线连接的两个码头编号以及航线长度(>0)。其中码头A编号为1,码头B编号为m。单位长度的运输费用为1。航线是双向的。再接下来一行是一个整数d,后面的d行每行是三个整数P(1<P<m),a,b(1≤a≤b≤n)。表示编号为P的码头从第a天到第b天无法装卸货物(含头尾)。同一个码头有可能在多个时间段内不可用。但任何时间都存在至少一条从码头A到码头B的运输路线。
输出格式
包括了一个整数表示最小的总成本。总成本=n天运输路线长度之和+K*改变运输路线的次数。
输入输出样例
输入 #1 复制
5 5 10 8
1 2 1
1 3 3
1 4 2
2 3 2
2 4 4
3 4 1
3 5 2
4 5 2
4
2 2 3
3 1 1
3 3 3
4 4 5
输出 #1 复制
32
说明/提示
【样例输入说明】
上图依次表示第1至第5天的情况,阴影表示不可用的码头。
【样例输出说明】
前三天走1-4-5,后两天走1-3-5,这样总成本为(2+2)*3+(3+2)*2+10=32。
_NOI导刊2010提高(01)
分析
可以分成两部分 最短路 + DP
因为图是变化的,复杂度允许,可以每天跑一遍最短路
d
p
[
i
]
dp[i]
dp[i]表示到第
i
i
i天总最小费用
则转移方程
d
p
[
i
]
=
m
i
n
(
d
p
[
i
]
,
d
p
[
j
−
1
]
+
(
i
−
j
+
1
)
∗
d
i
s
[
m
]
+
k
)
;
dp[i]=min(dp[i],dp[j-1]+(i-j+1)*dis[m]+k);
dp[i]=min(dp[i],dp[j−1]+(i−j+1)∗dis[m]+k);
代码
/********************
User:Mandy
Language:c++
Problem:luogu1772 物流运输
********************/
#include<bits/stdc++.h>
using namespace std;
const int maxn=105;
const int maxm=25;
const int maxe=10005;
int n,m,size,k,e,d,ans;
int first[maxm],dis[maxm],dp[maxn];
int waf[maxm][maxn];
bool vis[maxe],close[maxm];
typedef pair<int,int>pir;
priority_queue<pir,vector<pir>,greater<pir> >q;
struct Edge
{
int v,w,nt;
}edge[maxe<<1];
template<typename T>inline void read(T &x)
{
x=0;bool f=0;char ch=getchar();
while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
if(f)x=-x;
}
template<typename T>void putch(const T x)
{
if(x>9) putch(x/10);
putchar(x%10|48);
}
template<typename T>inline void put(const T x)
{
if(x<0) putchar('-'),putch(-x);
else putch(x);
}
void docu()
{
freopen("trans1.in","r",stdin);
}
void eadd(int u,int v,int w)
{
edge[++size].v=v;
edge[size].w=w;
edge[size].nt=first[u];
first[u]=size;
}
void readdata()
{
read(n);read(m);read(k);read(e);
for(int i=1;i<=e;++i)
{
int u,v,w;
read(u);read(v);read(w);
eadd(u,v,w);
eadd(v,u,w);
}
read(d);
for(int i=1;i<=d;++i)
{
int p,a,b;
read(p);read(a);read(b);
for(int j=a;j<=b;++j) waf[p][j]=1;
}
}
void dijkstra()
{
q.push(make_pair(0,1));
memset(dis,0x3f3f3f3f,sizeof(dis));
memset(vis,0,sizeof(vis));
dis[1]=0;
while(!q.empty())
{
int u=q.top().second;
q.pop();
if(vis[u]) continue;
vis[u]=1;
for(int i=first[u];i;i=edge[i].nt)
{
int v=edge[i].v,w=edge[i].w;
if(close[v]) continue;
if(dis[v]>dis[u]+w)
{
dis[v]=dis[u]+w;
q.push(make_pair(dis[v],v));
}
}
}
}
void work()
{
memset(dp,0x3f3f3f3f,sizeof(dp));
dp[0]=0;
for(int i=1;i<=n;++i)
{
memset(close,0,sizeof(close));
for(int j=i;j>=1;--j)
{
for(int l=1;l<=m;++l) if(waf[l][j]) close[l]=1;//j~i中所有至少有一天不能开的港口
dijkstra();//求j~i的共同最短路径
if(dis[m]==0x3f3f3f3f) break;//如果无法到达,则后面也无法到达
dp[i]=min(dp[i],dp[j-1]+(i-j+1)*dis[m]+k);
}
}
put(dp[n]-k);
}
int main()
{
// docu();
readdata();
work();
return 0;
}
最小生成树 + 动规
新三国争霸
传送门:codevs1403
题目
描述 Description
PP 特别喜欢玩即时战略类游戏,但他觉得那些游戏都有美中不足的地方。灾害总不降临道路,而只降临城市,而且道路不能被占领,没有保护粮草的真实性。于是他就研发了《新三国争霸》。
在这款游戏中,加入灾害对道路的影响(也就是一旦道路W[i,j]受到了灾害的影响,那么在一定时间内,这条路将不能通过)和道路的占领权(对于一条道路W[i,j],至少需要K[i,j]个士兵才能守住)。
PP可真是高手,不一会,就攻下了N-1座城市,加上原来的就有N座城市了,但他忽略了一点……那就是防守同样重要,不过现在还来的及。因为才打完仗所以很多城市都需要建设,PP估算了一下,大概需要T天。他现在无暇分身进攻了,只好在这T天内好好的搞建设了。所以他要派士兵占领一些道路,以确保任何两个城市之间都有路(不然敌人就要分而攻之了,是很危险的)。士兵可不是白干活的,每个士兵每天都要吃掉V的军粮。因为有灾害,所以方案可能有变化(每改变一次就需要K的军粮,初始方案也需要K的军粮)。
因为游戏是PP编的,所以他知道什么时候有灾害。PP可是一个很节约的人,他希望这T天在道路的防守上花最少的军粮。
输入格式 InputFormat
第一行有5个整数N,M,T,V,K。N表示有城市数,M表示道路数,T表示需要修养的天数,V表示每个士兵每天吃掉的军粮数,K表示修改一次花掉的军粮数。
以下M行,每行3个数A,B,C。表示A与B有一条路(路是双向的)需要C个士兵才能守住。
第M+2行是一个数P,表示有P个灾害。
以下P行,每行4个数,X,Y,T1,T2。表示X到Y的这条路,在T1到T2这几天都会受灾害。
输出格式 OutputFormat
T天在道路的防守上花费最少的军粮。
样例输入 SampleInput
3 3 5 10 30
1 2 1
2 3 2
1 3 4
1
1 3 2 5
样例输出 SampleOutput
180
数据范围和注释 Hint
对于所有数据:N<=300,M<=5000 ,T<=50,P<=8000
分析
最小生成树 + DP;
用
d
p
[
i
]
dp[i]
dp[i]表示到第
i
i
i天总共的最少费用
方程:(ans是第j~i天的共有的最小生成树最低费用)
d
p
[
i
]
=
m
i
n
(
d
p
[
i
]
,
d
p
[
j
−
1
]
+
(
i
−
j
+
1
)
∗
a
n
s
+
K
dp[i] = min(dp[i],dp[j - 1] + (i - j + 1) * ans + K
dp[i]=min(dp[i],dp[j−1]+(i−j+1)∗ans+K
代码
/*************************
User:Mandy.H.Y
Language:c++
Problem:three
Algorithm:
*************************/
#include<bits/stdc++.h>
#define Max(x,y) ((x) > (y) ? (x) : (y))
#define Min(x,y) ((x) < (y) ? (x) : (y))
using namespace std;
const int maxn = 305;
const int maxm = 5005;
int T,N,M,size,V,K,P,now,pre,ans;
int father[maxn];
int dp[55];
bool vis[maxn][maxn];
//因为是按边禁,就标记边而不是点 qwq
struct Val{
int u,v,w;
}val[maxm];
struct Dis{
int x,y,l,r;
}dis[8005];
template<class T>inline void read(T &x){
x = 0;bool flag = 0;char ch = getchar();
while(!isdigit(ch)) flag |= ch == '-',ch = getchar();
while(isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48),ch = getchar();
if(flag) x = -x;
}
template<class T>void putch(const T x){
if(x > 9) putch(x / 10);
putchar(x % 10 | 48);
}
template<class T>void put(const T x){
if(x < 0) putchar('-'),putch(-x);
else putch(x);
}
void file(){
freopen("three.in","r",stdin);
// freopen("three.out","w",stdout);
}
void add(int u,int v,int w){
val[++size].v = v;
val[size].u = u;
val[size].w = w;
}
bool cmp(const Val &a,const Val &b){
return a.w < b.w;
}
bool cmp1(const Dis &a,const Dis &b){
if(a.l != b.l) return a.l < b.l;
else return a.r < b.r;
}
void readdata(){
read(N);read(M);read(T);read(V);read(K);
for(int i = 1;i <= M; ++ i){
int u,v,w;
read(u);read(v);read(w);
add(u,v,w);
}
sort(val + 1,val + 1 + M,cmp);
read(P);
for(int i = 1;i <= P; ++ i){
read(dis[i].x); read(dis[i].y); read(dis[i].l); read(dis[i].r);
}
sort(dis + 1,dis + 1 + P,cmp1);
}
int find(int x){
return father[x] == x ? x : father[x] = find(father[x]);
}
void merge(int x,int y){
father[find(x)] = find(y);
}
int kruskal(int x,int y){
for(int i = 1;i <= N; ++ i) father[i] = i;
memset(vis,0,sizeof(vis));
for(int i = 1;i <= P; ++ i){
if(dis[i].l > y) break;
if(dis[i].l <= y && x <= dis[i].r){
vis[dis[i].x][dis[i].y] = 1;
vis[dis[i].y][dis[i].x] = 1;
}
}
int cnt = 0,cost = 0;
for(int i = 1;i <= M; ++ i){
int v = val[i].v;
int u = val[i].u;
int w = val[i].w;
if(vis[u][v]) continue;
if(find(u) != find(v)){
merge(u,v);
cost += w * V;
++cnt;
}
if(cnt == N - 1) return cost;
}
return -1;
}
void work(){
memset(dp,0x3f3f3f3f,sizeof(dp));
dp[0] = 0;
for(int i = 1;i <= T; ++ i){
for(int j = i;j >= 1; -- j){
int ans = kruskal(j,i);
if(ans == -1) continue;
dp[i] = min(dp[i],dp[j - 1] + (i - j + 1) * ans + K);
}
}
put(dp[T]);
}
int main(){
// file();
readdata();
work();
return 0;
}