题意:某人要去给n个人送礼,每个人在不同的地方,且他们之间共有m条单向边。另外每个人会提供一个舒心值(也即点权)。现此人任选某个人开始,可以沿单向边访问下一个人。对每个节点可以选择给他礼物并获得相应舒心值或者什么也不做(其中没给过礼物的点可以访问多次,但给过礼物的点只能访问一次),求他能获得的最大舒心值。
思路:首先可以证明在一个强连通分量中可以获得所有正的权值并舍弃所有负的权值。然后通过缩点形成DAG,其中DAG中每个点的权值是其对应原图中按个强连通分量中所有正权值之和。然后在DAG上求一条路径使其通过点得权值最大。直接通过dp便可求。也可以在入度为0的点上做SPFA来求。
建立新图时用邻接表还是邻接矩阵纠结了一下。感觉用临界表会产生重复边(虽然无大碍),用邻接矩阵开的太大了。最后试了一下邻接矩阵开小一点能过,否则MLE。
3249是此题的简化版,给定一个点带权有向无环图,求从某起点开始到终点的最长路,直接dp即可(SPFA据说会超时)。
#include <stdio.h>
#include <string.h>
#define N 30005
#define INF 0x3fffffff
#define clr(s,t) memset(s,t,sizeof(s))
#define min(a,b) ((a)<(b)?(a):(b))
#define max(a,b) ((a)>(b)?(a):(b))
struct edge{
int y,next;
}e[150005];
int w[N],dfn[N],low[N],first[N],stack[N],inst[N],strong[N],g[N>>4][N>>4];
int t[N>>4],dp[N>>4];
int n,m,id,top,con,st;
void init(){
top = id = con = st = 0;
clr(first, -1);
clr(dfn, -1);
clr(inst, 0);
clr(g, 0);
clr(t, 0);
clr(dp, 0);
}
void add(int x,int y){
e[top].y = y;
e[top].next = first[x];
first[x] = top++;
}
void tarjan(int x){
int i,y;
dfn[x] = low[x] = ++id;
stack[st++] = x;
inst[x] = 1;
for(i = first[x];i!=-1;i=e[i].next){
y = e[i].y;
if(dfn[y] == -1){
tarjan(y);
low[x] = min(low[x], low[y]);
}else if(inst[y])
low[x] = min(low[x], dfn[y]);
}
if(dfn[x] == low[x]){
con++;
do{
strong[stack[--st]] = con;
inst[stack[st]] = 0;
}while(x != stack[st]);
}
}
int test(int x){
int i;
if(dp[x])
return dp[x];
for(i = 1;i<=con;i++)
if(g[x][i])
dp[x] = max(dp[x],test(i));
return dp[x] += t[x];
}
int main(){
while(scanf("%d %d",&n,&m) != EOF){
int i,j,a,b,res=0;
init();
for(i = 0;i<n;i++)
scanf("%d",&w[i]);
for(i = 0;i<m;i++){
scanf("%d %d",&a,&b);
add(a,b);
}
for(i = 0;i<n;i++)
if(dfn[i] == -1)
tarjan(i);
for(i = 0;i<n;i++){
if(w[i]>0)
t[strong[i]]+=w[i];
for(j = first[i];j!=-1;j=e[j].next)
if(strong[i] != strong[e[j].y])
g[strong[i]][strong[e[j].y]] = 1;
}
for(i = 1;i<=con;i++){
j = test(i);
res = max(res,j);
}
printf("%d\n",res);
}
return 0;
}
3249:
#include <cstdio>
#include <queue>
#include <algorithm>
#include <cstring>
using namespace std;
#define INF 0x3fffffff
#define clr(s,t) memset(s,t,sizeof(s));
#define N 100005
struct edge{
int y,next;
}e[1000005];
int first[N],top,n,m,s[N],dp[N],d[N],g[N];
void add(int x,int y){
e[top].y = y;
e[top].next = first[x];
first[x] = top++;
}
int main(){
while(scanf("%d %d",&n,&m)!=EOF){
int i,a,b,now,res=-INF;
queue<int>q;
clr(first,-1);
clr(d,0);
clr(g, 0);
top = 0;
for(i = 1;i<=n;i++)
dp[i] = -INF;
for(i = 1;i<=n;i++)
scanf("%d",&s[i]);
for(i = 1;i<=m;i++){
scanf("%d %d",&a,&b);
add(a,b);
d[b]++;
g[a]++;
}
for(i = 1;i<=n;i++)
if(!d[i]){
dp[i] = s[i];
q.push(i);
}
while(!q.empty()){
now = q.front();
q.pop();
for(i = first[now];i!=-1;i=e[i].next){
dp[e[i].y] = max(dp[e[i].y],dp[now]+s[e[i].y]);
d[e[i].y]--;
if(d[e[i].y]==0)
q.push(e[i].y);
}
}
for(i = 1;i<=n;i++)
if(!g[i])
res = max(res,dp[i]);
printf("%d\n",res);
}
}