340. 通信线路
题意:
给定n个点m条边的无向图,边有边权。
你可以指定一条从点1到点n的路径,最多将k条边的权值变为0,然后这条路径的代价为路径上剩余边的最大边权。
问从1到n的最小花费是多少,如果1无法到达n则输出-1
数据范围:n,k<=1e3,m<=1e4
解法:
code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e3+5;
const int M=1e5+5;
const int inf=0x3f3f3f3f;
int head[N],nt[M<<1],to[M<<1],w[M<<1],cnt;
int d[N][N];
int n,m,k;
void add(int x,int y,int z){
cnt++;nt[cnt]=head[x];head[x]=cnt;to[cnt]=y;w[cnt]=z;
}
void dj(){
typedef pair<int,int> PI;
priority_queue<PI,vector<PI>,greater<PI> >q;
for(int i=1;i<=n;i++){
for(int j=0;j<=k;j++){
d[i][j]=inf;
}
}
d[1][0]=0;
q.push({d[1][0],1});
while(!q.empty()){
int x=q.top().second;q.pop();
int c=x/n;//层
x%=n;//点
for(int i=head[x];i;i=nt[i]){
int v=to[i];
int cost=max(w[i],d[x][c]);
if(d[v][c]>cost){//走当前层
d[v][c]=cost;
q.push({d[v][c],v+c*n});
}
if(c<k){//走下一层
if(d[v][c+1]>d[x][c]){
d[v][c+1]=d[x][c];
q.push({d[v][c+1],v+(c+1)*n});
}
}
}
}
}
signed main(){
cin>>n>>m>>k;
for(int i=1;i<=m;i++){
int a,b,c;cin>>a>>b>>c;
add(a,b,c);add(b,a,c);
}
dj();
int ans=inf;
for(int i=0;i<=k;i++){
ans=min(ans,d[n][i]);
}
if(ans==inf)ans=-1;
cout<<ans<<endl;
return 0;
}
341. 最优贸易
题意:
给n个点m条边的图,保证无自环和重边。
这 m 条道路中有一部分为单向通行的道路,一部分为双向通行的道路,双向通行的道路在统计条数时也计为1条。每个城市出售商品,同一种商品在同一个城市的买入价和卖出价始终是相同的。
商人阿龙来旅游,当他得知“同一种商品在不同城市的价格可能会不同”这一信息之后,便决定在旅游的同时,利用商品在不同城市中的差价赚一点旅费。
设n个城市的标号从 1~n,阿龙决定从1号城市出发,并最终在 n 号城市结束自己的旅行。
在旅游的过程中,任何城市可以被重复经过多次,但不要求经过所有 n 个城市。
阿龙通过这样的贸易方式赚取旅费:他会选择一个经过的城市买入他最喜欢的商品——水晶球,并在之后经过的另一个城市卖出这个水晶球,用赚取的差价当做旅费。
因为阿龙主要是来C国旅游,他决定这个贸易只进行最多一次,当然,在赚不到差价的情况下他就无需进行贸易。
现在给出 n 个城市的水晶球价格,m 条道路的信息(每条道路所连接的两个城市的编号以及该条道路的通行情况)。
请你告诉阿龙,他最多能赚取多少旅费。
解法:
y总的题解:
为什么不能用dj呢?
看到另外一种解法:
根据上面题解里面说的,这题主要是环的问题,因此可以先将环缩点,然后图就变成有向无环图了。
接着拓扑排序即可。
ps:
spfa解法:
求1到 i 的dmin[]用的是正向图,求n到 i 的dmax[]是反向图,共两次spfa
缩点+拓扑解法:
同样是双向图,缩点拓扑也都是两次,码量应该会大一点
代码用是spfa解法
code:
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
const int M=5e5+5;
vector<int>g[N],gg[N];
int dmi[N],dma[N];
int val[N];
int mark[N];
int n,m;
void spfa(int st,vector<int>g[N],int *d,int f){
for(int i=1;i<=n;i++){
d[i]=!f?1e9:0;
mark[i]=0;
}
queue<int>q;
q.push(st);
d[st]=val[st];
mark[st]=1;
while(!q.empty()){
int x=q.front();q.pop();
mark[x]=0;
for(int v:g[x]){
if(!f){//mi
if(d[v]>min(d[x],val[v])){
d[v]=min(d[x],val[v]);
if(!mark[v]){
mark[v]=1;
q.push(v);
}
}
}else{//ma
if(d[v]<max(d[x],val[v])){
d[v]=max(d[x],val[v]);
if(!mark[v]){
mark[v]=1;
q.push(v);
}
}
}
}
}
}
signed main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)scanf("%d",&val[i]);
for(int i=1;i<=m;i++){
int a,b,c;scanf("%d%d%d",&a,&b,&c);
if(c==1){//单向
g[a].push_back(b);
gg[b].push_back(a);
}else if(c==2){//双向
g[a].push_back(b);
g[b].push_back(a);
gg[a].push_back(b);
gg[b].push_back(a);
}
}
spfa(1,g,dmi,0);
spfa(n,gg,dma,1);
int ans=0;
for(int i=1;i<=n;i++){
ans=max(ans,dma[i]-dmi[i]);
}
printf("%d\n",ans);
return 0;
}
342. 道路与航线
题意:
有n个城市,r条双向边,p条单向边,起点为st
边有边权,单向边的边权可能为负数,保证单向边如果是从a到达b,那么不存在从b回到a的路径,即不存在负环。
保证不存在环
问从起点到每个点的最短距离是多少
数据范围:n<=3e4,r,p<=5e4
解法:
显然是一道单源最短路,存在负边权就用spfa。
但是这题会卡spfa
看到了别人的SLF优化:
ps:
spfa的优化都太玄学了,感觉知道一下就行了
code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e5+5;
const int M=1e6+6;
const int inf=1e18;
int head[N],nt[M],to[M],w[M],cnt;
int mark[N];
int dist[N];
int n,r,p,s;
void add(int x,int y,int z){
cnt++;nt[cnt]=head[x];head[x]=cnt;to[cnt]=y;w[cnt]=z;
}
void spfa(int st){
deque<int>q;
for(int i=1;i<=n;i++){
dist[i]=inf;
}
dist[st]=0;
mark[st]=1;
q.push_back(st);
while(!q.empty()){
int x=q.front();q.pop_front();
mark[x]=0;
for(int i=head[x];i;i=nt[i]){
int v=to[i];
if(dist[v]>dist[x]+w[i]){
dist[v]=dist[x]+w[i];
if(!mark[v]){
mark[v]=1;
if(!q.empty()&&dist[v]<dist[q.front()]){
q.push_front(v);
}else{
q.push_back(v);
}
}
}
}
}
}
signed main(){
cin>>n>>r>>p>>s;
for(int i=1;i<=r;i++){//双向
int a,b,c;cin>>a>>b>>c;
add(a,b,c);
add(b,a,c);
}
for(int i=1;i<=p;i++){
int a,b,c;cin>>a>>b>>c;
add(a,b,c);
}
spfa(s);
for(int i=1;i<=n;i++){
if(dist[i]==inf){
puts("NO PATH");
}else{
cout<<dist[i]<<endl;
}
}
return 0;
}
343. 排序
题意:
给定 n 个变量和 m 个不等式。其中 n 小于等于26,变量分别用前 n 的大写英文字母表示。
不等式之间具有传递性,即若 A>B 且 B>C ,则 A>C。
请从前往后遍历每对关系,每次遍历时判断:
1.如果能够确定全部关系且无矛盾,则结束循环,输出确定的次序;
2.如果发生矛盾,则结束循环,输出有矛盾;
3.如果循环结束时没有发生上述两种情况,则输出无定解。
数据范围:n<=26,即最多只有A-Z
解法:
观察到数据范围较小,且题目显然是传递关系,因此可以用floyd传递闭包(拓扑排序也可以)
每次新添加一条边都floyd一次,如果存在自环则冲突,否则如果g(i,j)!=g(j,i)对于所有i!=j都成立,则可以确定次序
code:
#include<bits/stdc++.h>
using namespace std;
const int N=105;
bool in[N],vis[N];
bool g[N][N];
int n,m;
void floyd(){//插点
for(int k=1;k<=n;k++){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
g[i][j]|=(g[i][k]&g[k][j]);
}
}
}
}
int check(){
for(int i=1;i<=n;i++){//判断是否存在自环,即矛盾
if(g[i][i])return -1;
}
int ok=1;
for(int i=1;i<=n&&ok;i++){//判断是否能确定关系,即满足g[i][j]!=g[j][i]
for(int j=1;j<=n&&ok;j++){
if(i==j)continue;
if(g[i][j]==g[j][i]){
ok=0;
}
}
}
if(ok)return 1;//可以确定
return 0;//不能确定
}
void print(){//暴力输出入度为0的未标记点
memset(in,0,sizeof in);
for(int i=1;i<=n;i++){
if(vis[i])continue;
for(int j=1;j<=n;j++){
if(vis[j])continue;
if(g[i][j])in[j]=1;
}
}
for(int i=1;i<=n;i++){
if(vis[i])continue;
if(!in[i]){
vis[i]=1;
printf("%c",i+'A'-1);
}
}
}
signed main(){
while(scanf("%d%d",&n,&m)!=EOF){
if(!n&&!m)break;
memset(vis,0,sizeof vis);
memset(g,0,sizeof g);
int f=0;
for(int i=1;i<=m;i++){
char s[4];scanf("%s",s);
if(f)continue;//如果已经得出结果则不处理数据
int a=s[0]-'A'+1,b=s[2]-'A'+1;
g[a][b]=1;
floyd();
f=check();
if(f==1)f=i;//记录成功位置
if(f==-1)f=-i;//记录矛盾位置
}
if(f<0){//矛盾
printf("Inconsistency found after %d relations.\n",-f);
}else if(f==0){//无法确定
printf("Sorted sequence cannot be determined.\n");
}else{//可以确定
printf("Sorted sequence determined after %d relations: ",f);
for(int i=1;i<=n;i++)print();//输出路径
puts(".");
}
}
return 0;
}