题解 P3116 【[USACO15JAN]约会时间Meeting Time】
即将退役的选手来写篇题解
此题做法:拓扑排序 + DP
首先,根据题意我们可以得出,这是一张有向无环图(DAG),那我们也不难想到拓扑排序,同时做一遍 DP 。
由于数据范围比较小,可以设计出如下的 DP 模型-:
状态:
转移方程:
边界:
目标状态:
注意到有两个人要走,所以我们开两个 DP 数组进行转移。最后求出最小的
具体实现请看代码:
#include <cstdio>
#include <queue>
#define rint register int //卡常加速
#define MAXN 105
using namespace std;
queue <int> q; //用队列进行拓扑排序
int n,m,cnt,head[MAXN],ind[MAXN];
//n:结点数 m:边数 head,cnt:链式前向星存图用 ind: 存储结点入度
bool f1[MAXN][MAXN*MAXN],f2[MAXN][MAXN*MAXN];
//DP数组
struct Edge{
int next,to,w1,w2;
}edge[MAXN*MAXN];
//链式前向星,w1、w2分别存储两个人走这条边所需的时间
inline void add(int u,int v,int w1,int w2){
edge[++cnt].next=head[u];
edge[cnt].to=v;
edge[cnt].w1=w1;
edge[cnt].w2=w2;
head[u]=cnt;
} //加边
int main(){
scanf("%d %d",&n,&m);
for(rint i=1;i<=m;++i){
int u,v,w1,w2;
scanf("%d %d %d %d",&u,&v,&w1,&w2);
add(u,v,w1,w2); //加边
++ind[v]; //统计入度
}
for(rint i=1;i<=n;++i)
if(!ind[i]) q.push(i); //入度为0的点入队
f1[1][0]=f2[1][0]=1; //DP边界
while(!q.empty()){ //拓扑排序板子
int u=q.front();
q.pop();
for(rint i=head[u];i;i=edge[i].next){
int v=edge[i].to;
for(rint j=0;j<=10000;++j){
if(j-edge[i].w1>=0) f1[v][j]|=f1[u][j-edge[i].w1]; //转移,注意判断j-edge[i].w>=0
if(j-edge[i].w2>=0) f2[v][j]|=f2[u][j-edge[i].w2];
}
if(--ind[v]==0) q.push(v);
}
}
for(rint i=0;i<=10000;++i)
if(f1[n][i]&&f2[n][i]){
printf("%d",i); //输出
return 0;
}
printf("IMPOSSIBLE"); //判断无解情况
return 0;
}