线性dp的本质其实就是DAG上递推,而最短路的本质其实就是dp,而最短路的dp其实就是处理有环图的一种dp处理方法。首先最短路是基于贪心的dp,如果是不基于贪心,其实本质就是跑n次o(m)的dp转移,但是由于有了贪心加上优先队列优化,就成了m*logn的,而这种贪心应用于某些有环的dp上就成了最短路dp问题。
题意:
给出 n 个点和 m 条边的有向图,每条边的长度为 1 ,有一个属性由 0 或 1 表示,现在需要给每个节点赋值,使得:
1.如果点 u 的权值为 0 ,则 u 只能走 ( u , v ) 且这条边的属性为 0 的边。
2.如果点 u 的权值为 1 ,则 u 只能走 ( u , v ) 且这条边的属性为 1 的边
问如何赋值能让点 1 到点 n 的最短路最大,输出一种构造方案。
思路:
这明显是最短路dp,而且得倒着跑,从n跑到1,然后dis[i][j]代表到达第i点且第i个点选择为j状态(0或者1)的最短路的最大值。明显,如果从1个点转移到另一个点,贪心的想肯定是得把这个点的max(dis[i][0],dis[i][1])作为当前的最短路,因为这样才能使得最短路最大。然后就跑最短路就行,因为边权为1,所以直接bfs即可。最后输出构造方案,就贪心即可。
#include<iostream>
#include<cstdio>
#include<vector>
#include<queue>
#include<cstring>
using namespace std;
const int MAX_N=501000;
const int INF=0x3f3f3f3f;
vector<int>v[MAX_N],w[MAX_N];
struct skt{
int x,y;
};
int dis[MAX_N][2];
int tox[MAX_N][2],toy[MAX_N][2];
int n;
int b[MAX_N];
void bfs(){
int i;
queue<skt>q;
skt t;
memset(dis,INF,sizeof(dis));
t.x=n,t.y=0;
q.push(t);
t.x=n,t.y=1;
q.push(t);
dis[n][0]=0;
dis[n][1]=0;
while(!q.empty()){
skt t=q.front();
q.pop();
int x=t.x,d=t.y;
for(i=0;i<v[x].size();i++){
int y=v[x][i];
int k=w[x][i];
if(dis[y][k]>max(dis[x][d],dis[x][d^1])+1){
dis[y][k]=max(dis[x][d],dis[x][d^1])+1;
tox[y][k]=x;
if(dis[x][d]>dis[x][d^1])
toy[y][k]=d;
else
toy[y][k]=d^1;
skt t;t.x=y,t.y=k;
q.push(t);
}
}
}
}
void dfs(int x,int y){
if(x==0&&y==0)
return ;
b[x]=y;
dfs(tox[x][y],toy[x][y]);
}
int main(void){
int m,i,x,y,t;
scanf("%d%d",&n,&m);
for(i=1;i<=m;i++){
scanf("%d%d%d",&x,&y,&t);
if(x==y)
continue;
v[y].push_back(x);
w[y].push_back(t);
}
bfs();
if(dis[1][0]==INF||dis[1][1]==INF)
printf("-1\n");
else
printf("%d\n",max(dis[1][0],dis[1][1]));
for(i=1;i<=n;i++){
if(dis[i][0]>dis[i][1])
b[i]=0;
else
b[i]=1;
printf("%d",b[i]);
}
return 0;
}
因篇幅问题不能全部显示,请点此查看更多更全内容