38 条题解
-
4
ToSoul LV 5 @ 7 年前
这道题的题意就不多说了,主要说下这里的两个主流做法。
1.DP:
其实这就是我最初的想法,不如让我们想想,对于一个强联通分量而言,我们想从哪里进就进,想从哪里出就出,而他们中的最小值或最大值我们是可以随便取的。当把这些强联通分量进行了缩点操作后,他们自然就变成了一个 普通的点 (只不过保存最小值和最大值),而在整个图则变成了一个 DAG 图,不难想到以 F[i](取当前城市作为出售处的最大利润) 的状态设计,然后先处理 Z[i] (从1所属的强联通点到i所属的强联通点中作为购入处的最小代价),再跑一遍 F[i] 即可。2.SPFA:
这是我从网上看到的解法,是在是佩服,对所有的有向边分别以 原方向 和 反方向 建两个图(无向边...),分别从 1点 和 N点 跑一遍 SPFA(SPFA的状态存的是经过点的最值,正向存最小值,反向存最大值,这一切目的都是为了保证购入点在卖出点前操作) ,然后枚举 交接点 ,算出两者之和的最大值即可。SPFA代码:
正反图,正反SPFA
这个还正常些...DP代码:
Tarjan缩点+Dp
DP我是用SPFA跑的,我跑BFS要挂... -
16 年前@
不带任何高级技巧的搜索+乱搞哈希
适合蒟蒻阅读
First:
我不会链式前向星,但是邻接矩阵肯定会爆,所以用了很神奇的结构体存图
只存了最多500个点,怕爆空间,可以水过。
Second:
搜索的时候用一个sta变量表示阶段。
通过阅读可得,贸易可以分为三个阶段:
买珠宝、卖珠宝、去终点
由于三个阶段是顺序做的事情,所以可以分别搜索
最后关于判重:乱搞哈希
我搜索时用到的三个变量:现在所在的地点now,拥有的钱money,以及现处阶段sta。于是就乱搞了这个判重语句:
#完整AC程序
-
16 年前@
tarjan缩点成为DAG图,然后记忆化搜索
-
16 年前@
看了一下题解区。。。没发现用C的,我先来一发~
正向spfa+反向bfs==ACMay the father of understanding guide U !
-
17 年前@
题目大意:
给你一个图,他又n个点和m条边。这些边可能是双向的也可能是单向的。先在,你要从1号点出发,要你到n号点上去,且不能经过所有点。每个点可以经过多次。现在,由于你知道了每个点上的水晶球的价格是不相同的,所以你要去做一次买卖:在一个点
上买进一个水晶球且在另一个点上卖出那个水晶球。他要求买卖获得的差价最高是多少。题解思路:
输入的存法:v[i][j]表示第i个点的第j条路线的另外一个端点。
u[i]j表示第j条能到达i号点的路的另外一个端点。先用一个接近与SPFA和BFS的东西来预处理出从一号点到x号点这条路径上能所买到的水晶球的最低价格,用dis[x]来存。弄完以后,我们有DFS来找:找过的就return,没找过就取做大值max(ans,a[x]-dis[x]);
最后输出ans程序:
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <float.h>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <set>
#include <queue>
#include <limits>
#include <deque>
#include <locale>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <wchar.h>
#include <wctype.h>
#include <algorithm>
#include <bitset>
#include <map>
#include <iomanip>
#include <ios>
#include <iostream>
#include <vector>
#include <cwchar>
#include <cwctype>
#define mp make_pair
#define fs first
#define se second
#define memset(a,t) memset(a,t,sizeof(a))
#define all(v) v.begin(),v.end()
#define eprintf(...) fprintf(stderr, VA_ARGS),fflush(stderr)
#define MN 0LL
#define Mx 200000005
#define Mn -Mx
#define MX 20000000000000005
using namespace std;
int readint(){
char c;
while(c=getchar(),(c<'0'||c>'9')&&c!='-');
bool flag=(c=='-');
if(flag)c=getchar();
int x=0;
while(c>='0'&&c<='9'){
x=x*10+c-48;
c=getchar();
}
return flag?-x:x;
}
inline string tos(int x){
string s;
while(x) s=(char)(x%10+'0')+s,x/=10;
return s;
}
int n,m;
int a[100005];
vector<int> v[100005];
vector<int> u[100005];
int dis[100005];
queue<int> q;
int ans=0;
bool vd[100005];
inline void dfs(int x){
if(vd[x]) return;
vd[x]=1;
ans=max(ans,(a[x]-dis[x]));
for(int i=0;i<u[x].size();i++){
dfs(u[x][i]);
}
}
int main(){
int i,j,x,y,z,p;
cin>>n>>m;
for(i=0;i<n;i++) cin>>a[i];
for(i=0;i<m;i++){
cin>>x>>y>>z;
x--,y--,z--;
v[x].push_back(y);
u[y].push_back(x);
if(z) v[y].push_back(x),u[x].push_back(y);
}
memset(dis,63);
dis[0]=a[0];
q.push(0);
while(!q.empty()){
p=q.front();
q.pop();
dis[p]=min(dis[p],a[p]);
for(i=0;i<v[p].size();i++){
x=v[p][i];
if(dis[x]>dis[p]){
dis[x]=dis[p];
q.push(x);
}
}
}
// for(i=0;i<n;i++) cout<<dis[i]<<' ';
// cout<<endl;
dfs(n-1);
cout<<ans<<endl;
return 0;}
-
06 年前@
反向DFS+正向DFS(深搜的点能够达到n)=AC
-
07 年前@
呵呵呵~~想了1小时没想出来,后来无意中看到了SPFA4个字母,就AC了~~!
-
07 年前@
SPFA*2
-
07 年前@
-
09 年前@
评测结果
编译成功
测试数据 #0: Accepted, time = 0 ms, mem = 11892 KiB, score = 10
测试数据 #1: Accepted, time = 0 ms, mem = 11892 KiB, score = 10
测试数据 #2: Accepted, time = 15 ms, mem = 11888 KiB, score = 10
测试数据 #3: Accepted, time = 93 ms, mem = 11924 KiB, score = 10
测试数据 #4: Accepted, time = 109 ms, mem = 11988 KiB, score = 10
测试数据 #5: Accepted, time = 0 ms, mem = 11888 KiB, score = 10
测试数据 #6: Accepted, time = 15 ms, mem = 11892 KiB, score = 10
测试数据 #7: Accepted, time = 46 ms, mem = 11920 KiB, score = 10
测试数据 #8: Accepted, time = 140 ms, mem = 11996 KiB, score = 10
测试数据 #9: Accepted, time = 109 ms, mem = 11984 KiB, score = 10
Accepted, time = 527 ms, mem = 11996 KiB, score = 100
代码//正向SPFA 获得最小 买入价格
//反向SPFA 获得最大 卖出价格#include <cstdio>
#include <queue>//#define debug
using std::queue;
struct edge{
int d;
struct edge *link;
};int top=1,n,m;
int price[100010];
edge graph[100010]={0};
edge graph2[100010]={0};
edge node[500050*2];void read(int s,int d){
edge *l;
l=&node[top];
l->d=d;
l->link=graph[s].link;
graph[s].link=l;
top++;
}void read2(int s,int d){
edge *l;
l=&node[top];
l->d=d;
l->link=graph2[s].link;
graph2[s].link=l;
top++;
}void build(){
scanf("%d%d",&n,&m);
int s,d,z;
for(int i=1;i<=n;i++)
scanf("%d",&price[i]);for(int i=1;i<=m;i++){
scanf("%d%d%d",&s,&d,&z);
read(s,d);
read2(d,s);
if(z==2){
read(d,s);
read2(s,d);
}
}
}//SPFA
int inque[100010]={0};
int dist[100010];
queue <int> q;void spfa(int s){
for(int i=1;i<=n;i++)
dist[i]=9999;
q.push(s);
inque[s]=1;
dist[s]=price[s];
edge *l;
int t;
while(!q.empty()){
t=q.front();
q.pop();
inque[t]=0;
l=graph[t].link;
int minthis;
while(l){
minthis=dist[t]<price[l->d]?dist[t]:price[l->d];
if(minthis<dist[l->d]){
dist[l->d]=minthis;if(!inque[l->d]){
q.push(l->d);
inque[l->d]=1;
}
}
l=l->link;
}
}
}int dist2[100010];
void spfa2(int s){
for(int i=1;i<=n;i++)
dist2[i]=-9999;
q.push(s);
inque[s]=1;
dist2[s]=price[s];
edge *l;
int t;
while(!q.empty()){
t=q.front();
q.pop();
inque[t]=0;
l=graph2[t].link;
int maxthis;
while(l){
maxthis=dist2[t]>price[l->d]?dist2[t]:price[l->d];
if(maxthis>dist2[l->d]){
dist2[l->d]=maxthis;if(!inque[l->d]){
q.push(l->d);
inque[l->d]=1;
}
}
l=l->link;
}
}
}int calculate(){
int money[100010];
for(int i=1;i<=n;i++)
money[i]=dist2[i]-dist[i];
int maxx=-9999;
for(int i=1;i<=n;i++)
maxx=maxx>money[i]?maxx:money[i];
if(maxx<0)
maxx=0;return maxx;
}int main(){
#ifdef debug
freopen("in.txt","r",stdin);
#endifbuild();
spfa(1); //正向SPFA
spfa2(n); //反向SPFA
printf("%d",calculate());
return 0;
} -
09 年前@
加边的时候存一张反图,从n开始dfs 把所有能够到达n的点打上标记
然后从1开始spfa 用dis[i]表示到达i点之前可能获得的最小价格水晶球 然后SPFA搞一搞
最后扫一遍所有打过标记的点 ans=max(ans,v[i]-dis[i]) (i点可以到达n) 注意不要忘了判断能否到达n表示被坑好多次#include <cstdio>
#include <algorithm>
#include <queue>
using namespace std;
const int N=100010,INF=2099999999;
int e[N*10],pre[N*10],now[N],tail,n,m,x,y,z,v[N],inq[N],dis[N],ans,e2[N*10],pre2[N*10],now2[N],tail2,vis[N];//dis[i]表示到城市i前可以获得的最小价值的水晶球void add(int u,int v){e[++tail]=v;pre[tail]=now[u];now[u]=tail;}
void add2(int u,int v){e2[++tail2]=v;pre2[tail2]=now2[u];now2[u]=tail2;}void dfs(int a){
if(vis[a])return;vis[a]=true;
for(int i=now2[a];i;i=pre2[i]) dfs(e2[i]);
}int main(){
freopen("1754.in","r",stdin);freopen("1754.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&v[i]),dis[i]=INF;
for(int i=1;i<=m;i++) {
scanf("%d%d%d",&x,&y,&z);
if(z==1) add(x,y),add2(y,x);else add(x,y),add2(y,x),add(y,x),add2(x,y);
}
dfs(n);
queue<int> q;
q.push(1);inq[1]=true;dis[1]=v[1];
while(!q.empty()){
int x=q.front();q.pop();inq[x]=false;
for(int i=now[x];i;i=pre[i]){
if(dis[x] < dis[e[i]] || v[e[i]]<dis[e[i]]){
dis[e[i]]=min(dis[e[i]],v[e[i]]);
dis[e[i]]=min(dis[e[i]],dis[x]);
if(!inq[e[i]]) {
q.push(e[i]);inq[e[i]]=true;
}
}
}
}
for(int i=1;i<=n;i++) if(vis[i]) ans=max(ans,v[i]-dis[i]);
printf("%d",ans);
return 0;
} -
09 年前@
反向BFS去掉不能到达的点,正向SPFA记录每个点,以这个点为卖出的话,能够买入的最小钱,即来的路上的最小价格。时间是O(n)的
-
09 年前@
/*
Author : Slience_K
Belong : C++
Pro : NOIP 2009 - 3*/
#include <cstdio>
#include <vector>
#include <algorithm>
#define sz 100005
using namespace std;
int n , m , u , v , w , ans;
int maxn[ sz ] , minn[ sz ] , p[ sz ];
vector <int> map[ sz ];
vector <int> pic[ sz ];
void Dfs1( int x , int k ){
minn[ x ] = min( minn[ x ] , k );
for( int i = 0 ; i < map[ x ].size() ; i++ ){
int v = map[ x ][ i ];
if ( minn[ v ] > k ) Dfs1( v , min( k , p[ v ] ) );
}
}
void Dfs2( int x , int k ){
maxn[ x ] = max( maxn[ x ] , k );
for( int i = 0 ; i < pic[ x ].size() ; i++ ){
int v = pic[ x ][ i ];
if ( maxn[ v ] < k ) Dfs2( v , max( k , p[ v ] ) );
}
}
int main(){
// freopen( "trade.in" , "r" , stdin );
// freopen( "trade.out" , "w" ,stdout );
scanf( "%d%d" , &n , &m );
for( int i = 1 ; i <= n ; i++ )
scanf( "%d" , &p[ i ] );
for( int i = 1 ; i <= m ; i++ ){
scanf( "%d%d%d" , &u , &v , &w );
map[ u ].push_back( v );
pic[ v ].push_back( u );
if ( w == 2 ){
map[ v ].push_back( u );
pic[ u ].push_back( v );
}
}
for( int i = 1 ; i <= n ; i++ )
maxn[ i ] = -sz , minn[ i ] = sz;
Dfs1( 1 , p[ 1 ] );
Dfs2( n , p[ n ] );
ans = 0;
for( int i = 1 ; i <= n ; i++ )
if (( minn[ i ] != sz )&&( maxn[ i ] != -sz )) ans = max( ans , maxn[ i ] - minn[ i ] );
printf( "%d" , ans );
// fclose( stdin );
// fclose( stdout );
return 0;
} -
09 年前@
编译成功
测试数据 #0: Accepted, time = 15 ms, mem = 16416 KiB, score = 10
测试数据 #1: Accepted, time = 0 ms, mem = 16420 KiB, score = 10
测试数据 #2: Accepted, time = 0 ms, mem = 16420 KiB, score = 10
测试数据 #3: Accepted, time = 15 ms, mem = 16428 KiB, score = 10
测试数据 #4: Accepted, time = 15 ms, mem = 16512 KiB, score = 10
测试数据 #5: Accepted, time = 0 ms, mem = 16416 KiB, score = 10
测试数据 #6: Accepted, time = 0 ms, mem = 16420 KiB, score = 10
测试数据 #7: Accepted, time = 15 ms, mem = 16428 KiB, score = 10
测试数据 #8: Accepted, time = 15 ms, mem = 16520 KiB, score = 10
测试数据 #9: Accepted, time = 31 ms, mem = 16496 KiB, score = 10
Accepted, time = 106 ms, mem = 16520 KiB, score = 100spfa维护到i时Min[i]最小值,Ans[i]最大值
-
09 年前@
用强联通分量+拓扑排序+dp AC,不过有180行
测试数据 #0: Accepted, time = 0 ms, mem = 4792 KiB, score = 10
测试数据 #1: Accepted, time = 4 ms, mem = 4792 KiB, score = 10
测试数据 #2: Accepted, time = 37 ms, mem = 5260 KiB, score = 10
测试数据 #3: Accepted, time = 515 ms, mem = 14180 KiB, score = 10
测试数据 #4: Accepted, time = 500 ms, mem = 14180 KiB, score = 10
测试数据 #5: Accepted, time = 0 ms, mem = 4828 KiB, score = 10
测试数据 #6: Accepted, time = 65 ms, mem = 5732 KiB, score = 10
测试数据 #7: Accepted, time = 250 ms, mem = 9608 KiB, score = 10
测试数据 #8: Accepted, time = 515 ms, mem = 14664 KiB, score = 10
测试数据 #9: Accepted, time = 468 ms, mem = 14652 KiB, score = 10时间不太好看,主要是因为用了Kosaraju,在数据量大的情况下2遍dfs是很慢的。改成Tarjan应该快很多。
用链表存边,正反向都要。边界点定义:typedef struct _node{
int to;
struct _node* next;
} node;强联通完之后再生成一张新图。由于有正向图、反向图、缩点图共三幅,所以把 添加边 写成了一个函数,以便重用:
void addEdge(node **G, int from, int to){
node p=G[from];
if(from==to) //请注意:缺少这个判断将造成自环,以致后面的入度计算有误。这是50分和100分的差别。
return;
if(G[from]==NULL){
G[from] = (node)malloc(sizeof(node*));
G[from]->to = to;
G[from]->next = NULL;
}else{
while(p->next != NULL){
if(p->to==to)
return;
p = p->next;
}
if(p->to==to)
return;
p->next = (node*)malloc(sizeof(node*));
p = p->next;
p->to = to;
p->next = NULL;
}
}在进行强联通分量求解时,需要对反向dfs增加一些步骤(以在行后注明)。使用Tarjan类同。
void dfsReversed(int indexSCC, int index, int size){ //indexSCC指的是这一个强联通分量在全局中是第几个,由主程序传入
node *p = reversedGraph[index];
if(searched[index])
return;
//price数组存储数据输入的每个城市的物价
value[indexSCC] = MAX(value[indexSCC], price[index]); //更新该强联通分量的最大收益
cost[indexSCC] = MIN(cost[indexSCC], price[index]); //更新该强联通分量的最小消耗
searched[index] = 1;
id[index] = indexSCC;
while(p!=NULL){
dfsReversed(indexSCC, p->to, size);
p = p->next;
}
}生成缩点图并计算入度:
for(i=0; i<numVertex; i++){
p = graph[i];
while(p!=NULL){
addEdge(newGraph, id[i], id[p->to]);
p = p->next;
}
}
for(i=0; i<numSCC; i++){
p = newGraph[i];
while(p!=NULL){
in[p->to]++;
p = p->next;
}
}最后,拓扑排序+动态规划。这里用到了队列以降低复杂度。把每一个入读为零的点加入队列,一直执行直至队列为空。
for(i=0; i<numSCC; i++)
best[i] = value[i]-cost[i]; //先进行预处理,防止孤立的点产生错误
push(id[0]); //push函数将数据加至队尾
while((i = shift())>=0){ //shift函数返回队头
p = newGraph[i];
in[i] = -1;
while(p!=NULL){
in[p->to]--;
if(in[p->to]==0){
in[p->to] = -1;
push(p->to);
}
/*
接下来两行是重点。首先更新一路走来到达p->to点的最小花费。
best[x]记录x点及之前经过的点出售后获利的最大值。
1. 若之前的点已出售,则 best[x] = best[y],其中y是x的前继
2. 若之前的点还未出售,则best[x] = value[x]-cost[x]
综合而言, best[x]为1,2情况的最大值
*/
cost[p->to] = MIN(cost[p->to], cost[i]);
best[p->to] = MAX(best[p->to], MAX(best[i], value[p->to]-cost[p->to]));
p = p->next;
}
} -
09 年前@
P1754最优贸易
Accepted记录信息
评测状态 Accepted
题目 P1754 最优贸易
递交时间 2015-08-03 14:57:12
代码语言 C++
评测机 Jtwd2
消耗时间 1040 ms
消耗内存 9580 KiB
评测时间 2015-08-03 14:57:14评测结果
编译成功
foo.cpp: In function 'void dfs(point*, int)':
foo.cpp:36:38: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
foo.cpp: In function 'void dfs2(point*)':
foo.cpp:47:39: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
foo.cpp: In function 'int main()':
foo.cpp:91:41: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]测试数据 #0: Accepted, time = 15 ms, mem = 4784 KiB, score = 10
测试数据 #1: Accepted, time = 15 ms, mem = 4780 KiB, score = 10
测试数据 #2: Accepted, time = 15 ms, mem = 4900 KiB, score = 10
测试数据 #3: Accepted, time = 124 ms, mem = 6608 KiB, score = 10
测试数据 #4: Accepted, time = 202 ms, mem = 8064 KiB, score = 10
测试数据 #5: Accepted, time = 15 ms, mem = 4796 KiB, score = 10
测试数据 #6: Accepted, time = 31 ms, mem = 5020 KiB, score = 10
测试数据 #7: Accepted, time = 93 ms, mem = 6344 KiB, score = 10
测试数据 #8: Accepted, time = 265 ms, mem = 9432 KiB, score = 10
测试数据 #9: Accepted, time = 265 ms, mem = 9580 KiB, score = 10
Accepted, time = 1040 ms, mem = 9580 KiB, score = 100
代码
#include <iostream>
#include <stdio.h>
#include <vector>
#include <algorithm>using namespace std;
int n , m;
int i;
int x , y;
int s;struct point
{
vector < point * > link;
vector < point * > link2;
int inprice;
int outprice;
int visit;
int num;
int reach;
};point line[100000 + 2];
vector < point * > order[100 + 2];void dfs( point * a , int v )
{
if( !a -> reach )
return;
if( a -> visit )
return;
a -> visit = 1;
a -> inprice = v;
int i;
for( i = 0 ; i < a -> link.size() ; i++ )
dfs( a -> link[i] , v );
return;
}void dfs2( point * a )
{
if( a -> reach )
return;
a -> reach = 1;
int i;
for( i = 0 ; i < a -> link2.size() ; i++ )
dfs2( a -> link2[i] );
return;
}int maxd;
int j;int max( int a , int b )
{
if( a > b )
return a;
return b;
}int main()
{
scanf( "%d %d" , &n , &m );
for( i = 1 ; i <= n ; i++ )
{
line[i].num = i;
scanf( "%d" , &line[i].outprice );
line[i].inprice = line[i].outprice;
}
for( i = 0 ; i < m ; i++ )
{
scanf( "%d %d %d" , &x , &y , &s );
if( s == 2 )
{
line[x].link.push_back( &line[y] );
line[y].link.push_back( &line[x] );
line[x].link2.push_back( &line[y] );
line[y].link2.push_back( &line[x] );
}
else
{
line[x].link.push_back( &line[y] );
line[y].link2.push_back( &line[x] );
}
}
for( i = 1 ; i <= n ; i++ )
order[ line[i].outprice ].push_back( &line[i] );
dfs2( &line[n] );
for( i = 1 ; i <= 100 ; i++ )
for( j = 0 ; j < order[i].size() ; j++ )
dfs( order[i][j] , i );
for( i = 1 ; i <= n ; i++ )
maxd = max( maxd , line[i].outprice - line[i].inprice );
cout << maxd << endl;
return 0;
}
读题的重要性! -
09 年前@
测试数据 #0: Accepted, time = 0 ms, mem = 5856 KiB, score = 10
测试数据 #1: Accepted, time = 0 ms, mem = 5852 KiB, score = 10
测试数据 #2: Accepted, time = 0 ms, mem = 5848 KiB, score = 10
测试数据 #3: Accepted, time = 46 ms, mem = 5852 KiB, score = 10
测试数据 #4: Accepted, time = 62 ms, mem = 5928 KiB, score = 10
测试数据 #5: Accepted, time = 0 ms, mem = 5852 KiB, score = 10
测试数据 #6: Accepted, time = 15 ms, mem = 5852 KiB, score = 10
测试数据 #7: Accepted, time = 31 ms, mem = 5848 KiB, score = 10
测试数据 #8: Accepted, time = 93 ms, mem = 5944 KiB, score = 10
测试数据 #9: Accepted, time = 78 ms, mem = 5920 KiB, score = 10
Accepted, time = 325 ms, mem = 5944 KiB, score = 100SPFA。
唉...C++党一定要注意用标准输入输出,不然过都过不了...
用数值代替指针的话会快一点。一次SPFA即可。
f[i]:到第i个城市的最大收益。
b[i]:到第i个城市的最小买入价格。每次到达一个城市有三个选择:先前到达时的最大收益、在之前经过的城市卖出获得的收益,在先前某个城市买入、该城市卖出的收益,f[TO[e]]=max(f[TO[e]],f[i],w[TO[e]]-b[i])。
如果收益增加了或者买入价格降低了就进行拓展。
最后f[N]就是最大收益。
参考:http://blog.sina.com.cn/s/blog_8442ec3b0100uosn.html
Block Code
#include<climits>
#include<cstring>
#include<queue>
#include<cstdio>
using namespace std;int N,M;
int NEXT[500003],TO[500003],V=0;
int w[100003],first[100003];
void add_edge(int n,int t)
{
V++;
TO[V]=t;
NEXT[V]=first[n];
first[n]=V;
}int f[MAXN],b[MAXN];
queue<int> q;
bool in[MAXN];int main()
{scanf("%d%d",&N,&M);
for(int i=1;i<=N;i++)
{
scanf("%d",&w[i]);
first[i]=0;
f[i]=INT_MIN;
b[i]=w[i];
}
for(int i=1;i<=M;i++)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
add_edge(x,y);
if(z==2) add_edge(y,x);
}memset(in,false,sizeof(in));
q.push(1);
f[1]=0;
in[1]=true;while(!q.empty())
{
int i=q.front();
q.pop();
in[i]=false;int e=first[i];
while(e!=0)
{
bool flag=false;if(f[TO[e]]<max(f[i],w[TO[e]]-b[i]))
{
f[TO[e]]=max(f[i],w[TO[e]]-b[i]);
flag=true;
}if(b[TO[e]]>b[i])
{
b[TO[e]]=b[i];
flag=true;
}if(flag&&!in[TO[e]])
{
q.push(TO[e]);
in[TO[e]]=true;
}
e=NEXT[e];
}
}printf("%d\n",f[N]);
return 0;
} -
010 年前@
严格O(nlogn+m)的算法.三个DFS.
读边的时候记下它的反向边存好.
第一次DFS,从起点开始,记录哪些节点能从起点到达.
第二次DFS,从终点,使用反向边,记录哪些节点能从终点到达.
把城市按照价值升序排序,开一个数组minv记录"对于所有到达此城市的路径,所能得出的最小购买价格".
从价值小的城市开始对每个城市做DFS,遍历它能达到的所有节点,给minv赋值.
很明显节点的minv有值以后就不再对它做DFS.因此,这n此操作复杂度是O(n+m).
最后统计,城市的minv与售价的差值,取最大输出.PS:ORZ SPFA的 强连通分量缩点的 拓扑图DP的 都是些啥
测试数据 #0: Accepted, time = 3 ms, mem = 33976 KiB, score = 10
测试数据 #1: Accepted, time = 15 ms, mem = 33976 KiB, score = 10
测试数据 #2: Accepted, time = 0 ms, mem = 33976 KiB, score = 10
测试数据 #3: Accepted, time = 15 ms, mem = 33984 KiB, score = 10
测试数据 #4: Accepted, time = 19 ms, mem = 33976 KiB, score = 10
测试数据 #5: Accepted, time = 0 ms, mem = 33984 KiB, score = 10
测试数据 #6: Accepted, time = 15 ms, mem = 33984 KiB, score = 10
测试数据 #7: Accepted, time = 31 ms, mem = 34104 KiB, score = 10
测试数据 #8: Accepted, time = 46 ms, mem = 34364 KiB, score = 10
测试数据 #9: Accepted, time = 66 ms, mem = 34364 KiB, score = 10
###Block code
const int INF=(1<<30)-1;
struct edge
{
int in;
edge*nxt;
}pool[4005000];
edge*et=pool;
edge*eds[100050];
edge*edp[100050];
void addedge(int i,int j)
{ et->in=j; et->nxt=eds[i]; eds[i]=et++; }
void addrevedge(int i,int j)
{ et->in=j; et->nxt=edp[i]; edp[i]=et++; }
#define FOREACH_EDGE(i,j) for(edge*i=eds[j];i;i=i->nxt)
#define FOREACH_REV_EDGE(i,j) for(edge*i=edp[j];i;i=i->nxt)int getint()
{
int res=0;
char c=getchar();
while( c<'0' || '9'<c ) c=getchar();
while( '0'<=c && c<='9' )
{ res=res*10+c-'0'; c=getchar(); }
return res;
}int v[100050];
int n,m;int st,ed;
bool rch[100050]; //can this node be reached?
void rchDFS(int x)
{
rch[x]=true;
FOREACH_EDGE(i,x)
if(!rch[i->in]) rchDFS(i->in);
}bool red[100050]; //can we reach terminal from this node?
void redDFS(int x)
{
red[x]=true;
FOREACH_REV_EDGE(i,x)
if(!red[i->in]) redDFS(i->in);
}int p[100050];
bool cmp(int a,int b)
{ return v[a]<v[b]; }int cv;
int minv[100050];
void DFS(int x)
{
minv[x]=cv;
FOREACH_EDGE(i,x)
if(minv[i->in]==INF) DFS(i->in);
}int main()
{
n=getint();
m=getint();
for(int i=0;i<n;i++)
v[i]=getint();
for(int i=0;i<m;i++)
{
int a=getint();
int b=getint();
int c=getint();
a--,b--;
addedge(a,b);
addrevedge(b,a);
if(c==2) addedge(b,a),addrevedge(a,b);
}st=0;
ed=n-1;rchDFS(st);
redDFS(ed);for(int i=0;i<n;i++)
p[i]=i,minv[i]=INF;
sort(p,p+n,cmp);for(int i=0;i<n;i++)
if(rch[p[i]] && minv[p[i]]==INF)
{ cv=v[p[i]]; DFS(p[i]); }int res=0;
for(int i=0;i<n;i++)
if(rch[i] && red[i] && v[i]-minv[i]>res )
res=v[i]-minv[i];printf("%d\n",res);
return 0;
} -
010 年前@
上次发的观看效果不太好,竟然不能编辑我自己的帖子,只能重发。
Tarjan强连通分量缩点+拓扑序dp
对缩点后的连通分量维护各分量内最大和最小值
测试数据 #0: Accepted, time = 0 ms, mem = 8788 KiB, score = 10
测试数据 #1: Accepted, time = 0 ms, mem = 8788 KiB, score = 10
测试数据 #2: Accepted, time = 15 ms, mem = 8880 KiB, score = 10
测试数据 #3: Accepted, time = 140 ms, mem = 10316 KiB, score = 10
测试数据 #4: Accepted, time = 171 ms, mem = 11424 KiB, score = 10
测试数据 #5: Accepted, time = 0 ms, mem = 8796 KiB, score = 10
测试数据 #6: Accepted, time = 15 ms, mem = 8932 KiB, score = 10
测试数据 #7: Accepted, time = 46 ms, mem = 9764 KiB, score = 10
测试数据 #8: Accepted, time = 140 ms, mem = 11740 KiB, score = 10
测试数据 #9: Accepted, time = 140 ms, mem = 11720 KiB, score = 10
Accepted, time = 667 ms, mem = 11740 KiB, score = 100
代码如下
###Block code
#include<cstdio>
#include<algorithm>
#include<vector>
#include<utility>
#include<queue>
using namespace std;
#define MAXN 100010
#define MAXM 1000010
#define INF 0x3f3f3f3f
typedef long long LL;
LL dp[MAXN],f[MAXN],a[MAXN],bin[MAXN],sout[MAXN];
int n,m,bcc[MAXN],dfsno,bccno,s[MAXN],stop,dfn[MAXN],low[MAXN],ind[MAXN];
bool ins[MAXN];
vector<vector<int> > adj(MAXN),map(MAXN);
void tarjan(int u) {
dfn[u]=low[u]=++dfsno;
ins[u]=true;
s[stop++]=u;
for(int i=0;i<adj[u].size();i++) {
int v=adj[u][i];
if(dfn[v]==0) {
tarjan(v);
low[u]=min(low[u],low[v]);
}
else if(ins[v]) low[u]=min(low[u],dfn[v]);
}
if(low[u]==dfn[u]) {
bccno++;
while(1) {
int p=s[--stop];
ins[p]=false;
bcc[p]=bccno;
bin[bccno]=min(bin[bccno],a[p]);
sout[bccno]=max(sout[bccno],a[p]);
if(p==u) break;
}
}
}
int main() {
fill(bin,bin+MAXN,INF);
int x,y,z;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
for(int i=0;i<m;i++) {
scanf("%d%d%d",&x,&y,&z);
adj[x].push_back(y);
if(z==2) adj[y].push_back(x);
}
tarjan(1);
for(int u=1;u<=n;u++) {
for(int i=0;i<adj[u].size();i++) {
int v=adj[u][i];
if(bcc[u]!=bcc[v]) {
ind[bcc[v]]++;
map[bcc[u]].push_back(bcc[v]);
}
}
}
queue<int> q;
q.push(bcc[1]);
dp[bcc[1]]=0ll;
f[bcc[1]]=bin[bcc[1]];
while(!q.empty()) {
int u=q.front();
q.pop();
for(int i=0;i<map[u].size();i++) {
int v=map[u][i];
f[v]=min(f[u],bin[v]);
dp[v]=max(dp[u],sout[v]-f[v]);
ind[v]--;
if(ind[v]==0) q.push(v);
}
}
printf("%lld\n",dp[bcc[n]]);
return 0;
} -
011 年前@
新手也来发个题解……
Tarjan缩强连通分量+拓扑序dp
对缩点后的连通分量保存各分量内最大和最小值测试数据 #0: Accepted, time = 0 ms, mem = 8788 KiB, score = 10
测试数据 #1: Accepted, time = 0 ms, mem = 8788 KiB, score = 10
测试数据 #2: Accepted, time = 15 ms, mem = 8880 KiB, score = 10
测试数据 #3: Accepted, time = 140 ms, mem = 10316 KiB, score = 10
测试数据 #4: Accepted, time = 171 ms, mem = 11424 KiB, score = 10
测试数据 #5: Accepted, time = 0 ms, mem = 8796 KiB, score = 10
测试数据 #6: Accepted, time = 15 ms, mem = 8932 KiB, score = 10
测试数据 #7: Accepted, time = 46 ms, mem = 9764 KiB, score = 10
测试数据 #8: Accepted, time = 140 ms, mem = 11740 KiB, score = 10
测试数据 #9: Accepted, time = 140 ms, mem = 11720 KiB, score = 10
Accepted, time = 667 ms, mem = 11740 KiB, score = 100#include<cstdio>
#include<algorithm>
#include<vector>
#include<utility>
#include<queue>
using namespace std;
#define MAXN 100010
#define MAXM 1000010
#define INF 0x3f3f3f3f
typedef long long LL;
LL dp[MAXN],f[MAXN],a[MAXN],bin[MAXN],sout[MAXN];
int n,m,bcc[MAXN],dfsno,bccno,s[MAXN],stop,dfn[MAXN],low[MAXN],ind[MAXN];
bool ins[MAXN];
vector<vector<int> > adj(MAXN),map(MAXN);
void tarjan(int u) {
dfn[u]=low[u]=++dfsno;
ins[u]=true;
s[stop++]=u;
for(int i=0;i<adj[u].size();i++) {
int v=adj[u][i];
if(dfn[v]==0) {
tarjan(v);
low[u]=min(low[u],low[v]);
}
else if(ins[v]) low[u]=min(low[u],dfn[v]);
}
if(low[u]==dfn[u]) {
bccno++;
while(1) {
int p=s[--stop];
ins[p]=false;
bcc[p]=bccno;
bin[bccno]=min(bin[bccno],a[p]);
sout[bccno]=max(sout[bccno],a[p]);
if(p==u) break;
}
}
}
int main() {
fill(bin,bin+MAXN,INF);
int x,y,z;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
for(int i=0;i<m;i++) {
scanf("%d%d%d",&x,&y,&z);
adj[x].push_back(y);
if(z==2) adj[y].push_back(x);
}
tarjan(1);
for(int u=1;u<=n;u++) {
for(int i=0;i<adj[u].size();i++) {
int v=adj[u][i];
if(bcc[u]!=bcc[v]) {
ind[bcc[v]]++;
map[bcc[u]].push_back(bcc[v]);
}
}
}
queue<int> q;
q.push(bcc[1]);
dp[bcc[1]]=0ll;
f[bcc[1]]=bin[bcc[1]];
while(!q.empty()) {
int u=q.front();
q.pop();
for(int i=0;i<map[u].size();i++) {
int v=map[u][i];
f[v]=min(f[u],bin[v]);
dp[v]=max(dp[u],sout[v]-f[v]);
ind[v]--;
if(ind[v]==0) q.push(v);
}
}
printf("%lld\n",dp[bcc[n]]);
return 0;
}