路径解析、TT 的魔法猫、TT 的旅行日记、TT 的美梦
作业:
TT 的魔法猫:
这道题是用的Floyd算法解决,Floyd算法主要用在
1、求多源最短路径
2、求传递闭包
因为胜负具有传递性,所以是一个传递闭包问题。目的是求有多少个场胜负无法预测,于是将插点求最短路的判断改为插点求&运算。即
map[i,j] = Max { map[i,k]&map[k,j] , map[i,j] };
Floyd算法空间复杂度O(n^2)。
时间时间复杂度为O(n^2)。
在初始化时建立邻接矩阵。这题空间复杂度是没问题的。但是这里由于Floyd算法复杂度为:O(n^3),所以要进行剪枝,观察发现因为是&运算,如果有一个是0,结果为0。也就是说如果map[i,k]=0;就不再枚举j。这样就不会TLE。
#include<bits/stdc++.h>
using namespace std;
#define maxn 505
int N;
int n,m;
int G[maxn][maxn];
int ans;
int main(){
scanf("%d",&N);
while(N--)
{
scanf("%d%d",&n,&m);
memset(G,0,sizeof(G));
for(int i=0;i<m;i++)
{ int a=0,b=0;
scanf("%d%d",&a,&b);
G[a][b]=1;//只需要输入一条边 总边=胜者+败者+无法预测*2
}
for(int k=1;k<=n;k++)
{
for(int i=1;i<=n;i++)
{
if(G[i][k]==1)//剪枝 这里为0 结果不可能为1了
for(int j=1;j<=n;j++)
{
if(G[k][j]==1)G[i][j]=1;
}
}
}
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(G[i][j]==1)ans++;
ans=n*(n-1)/2-ans;
printf("%d\n",ans);
ans=0;
}
return 0;
}
TT 的旅行日记:
这道题也是一个求最短路径问题。但是这道题不能直接用Dijkstra或者SPFA求。他有个问题是怎么处理商业线。因为在最短路中只能用一次商业线。所以将求起点到终点的最短路改为 :遍历所有商业线,求起点到商业线的一个点的最短路+终点到商业线的另一个点的最短路。再与起点到终点取最小值即可。
这里用堆优化的Dijkstra来做,建立最大堆,入堆的时候入距离的相反数。这样最大值变成了最小值,弹出的最大值其实就是最小值。这样就等于初始了最小堆。
但是这个题要有特别坑的一点:他存在这样一种情况:不用商业线到达不了终点的情况。所以要加以区分。
输出的时候也是有点问题:要从头到尾输出路径,这里可以存路径的前驱节点。然后从终点递归输出即可。
#include<bits/stdc++.h>
using namespace std;
#define maxn 505
#define inf 0x3f3f3f3f
#pragma warning(disable:4996)
int N,S,E;
int M,K;
int tot;int head[maxn];//用于前向星
int dis[maxn][2];int vis[maxn]; int father[maxn][2];
struct Edge{
int u,v,w,next;
}edge[maxn*maxn];
void add(int u,int v,int w)
{
edge[tot].u=u;edge[tot].v=v;edge[tot].w=w;
edge[tot].next=head[u];
head[u]=tot;
tot++;
}
priority_queue< pair<int,int> > q;
void dij(int s,int op){
memset(vis,0,sizeof(vis));
while(!q.empty()) q.pop();
q.push(make_pair(0,s));
dis[s][op]=0;
father[s][op]=s;
while(!q.empty())
{
int u=q.top().second;q.pop(); if(vis[u]==1)continue;vis[u]=1;
for(int i=head[u];i!=-1;i=edge[i].next)
{
int v=edge[i].v;int w=edge[i].w;
if(dis[v][op]>w+dis[u][op])
{
dis[v][op]=w+dis[u][op];
father[v][op]=u;
q.push(make_pair(-dis[v][op],v));
}
}
}
}
void out(int x){
if(father[x][0]==x)
{
printf("%d",x);return;
}
out(father[x][0]);
printf(" %d",x);
}
int main(){
int markk=0;
// freopen("out.txt","w",stdout);
while(scanf("%d%d%d",&N,&S,&E)==3)
{
scanf("%d",&M);int t1,t2,t3;
memset(head,-1,sizeof(head));
memset(dis,inf,sizeof(dis));
memset(father,-1,sizeof(father));
while(M--){scanf("%d%d%d",&t1,&t2,&t3);add(t1,t2,t3);add(t2,t1,t3);}
dij(S,0);
dij(E,1);
scanf("%d",&K);int a,b,c;
int hcu=-1;int hcv=-1;int min=dis[E][0];
while(K--)
{scanf("%d%d%d",&a,&b,&c);
if(dis[E][0]!=inf)
{
if(dis[a][0]+dis[b][1]+c<min)
{
hcu=a;hcv=b;
min=dis[a][0]+dis[b][1]+c;
}
if(dis[b][0]+dis[a][1]+c<min)
{
hcu=b;hcv=a;
min=dis[b][0]+dis[a][1]+c;
}
} else
{
//cout<<"bu可联通"<<endl;
if(dis[a][0]!=inf&&dis[b][1]!=inf)
{
hcu=a;hcv=b;min=dis[a][0]+dis[b][1]+c;
}
else if(dis[b][0]!=inf&&dis[a][1]!=inf)
{
hcu=b;hcv=a;min=dis[b][0]+dis[a][1]+c;
}
}
}
// for(int i=1;i<=N;i++)
// {
// for(int j=0;j<2;j++)
// {
// if(dis[i][j]==inf)cout<<"inf"<<" ";else cout<<dis[i][j]<<" ";
// }cout<<endl;
// }
if(hcu==-1)
{
if(markk==1) printf("\n");
out(E);printf("\n");
printf("Ticket Not Used\n");
printf("%d\n",min);
}
else
{
//cout<<hcu<<father[1][0]<<father[2][0]<<father[3][0]<<father[4][0]<<endl;
if(markk==1)printf("\n");
out(hcu);
for(int i=hcv;i!=E;i=father[i][1])printf(" %d",i);
printf(" %d\n",E);
printf("%d\n",hcu);
printf("%d\n",min);
}
markk=1;
}
// fclose(stdout);
return 0;
}
TT 的美梦:
这道题是使用了Bellman-Ford算法的队列优化即:SPFA算法。通常用于求含负权边的单源最短路径,以及判负权环。
SPFA算法和堆优化的Dijkstra算法很像,不同点在于SPFA算法可以重复入队,每次取出队首元素,该元素之后可能还会入队。Dijkstra则只能从堆中弹出一次。
这道题就是判断负权环的题目,存在负权环则最终权重一定为负无穷。由于图中的一条路径最长为n-1,所以如果路径大于等于n,就说明存在负权边。
当一个点入队n次后,则可以判断到该点存在负权边,于是该点能到达的所有点最短距离都为负无穷。所以可以从这个点进行DFS并标记。
#include<bits/stdc++.h>
using namespace std;//lightoj 1074
#define maxn 205
#define inf 0x3f3f3f3f
#pragma warning(disable:4996)
int N,M,Q;
int a[maxn];
int tot,head[maxn];//用于前向星
int vis[maxn];int dis[maxn]; int cnt[maxn];int fh[maxn];
struct Edge{
int u,v,w,next;
}edge[maxn*maxn*10];
void add(int u,int v,int w)
{
edge[tot].u=u;edge[tot].v=v;edge[tot].w=w;
edge[tot].next=head[u];
head[u]=tot;
tot++;
}
void dfs(int s){
fh[s]=1;//cout<<s<<" ";
for(int i=head[s];i!=-1;i=edge[i].next)
{
if(fh[edge[i].v]==0)
dfs(edge[i].v);
}
}
void spfa(int s)
{
memset(vis,0,sizeof(vis));
memset(dis,inf,sizeof(dis));
memset(cnt,0,sizeof(cnt));
memset(fh,0,sizeof(fh));
dis[s]=0;vis[s]=1;
queue<int>q;while(!q.empty())q.pop();
q.push(s);
while(!q.empty())
{ //cout<<"ffffffffffff"<<endl;
int u=q.front();q.pop();vis[u]=0;
for(int i=head[u];i!=-1;i=edge[i].next)
{
int v=edge[i].v;int w=edge[i].w;
if(dis[v]>dis[u]+w)
{
dis[v]=dis[u]+w;
cnt[v]=cnt[u]+1;
if(cnt[v]>=N){
dfs(v);
}
else if(vis[v]==0) q.push(v),vis[v]=1;
}
}
}
}
int main(){
int T;scanf("%d",&T);int _count=0;
while(T--)
{_count++;
memset(head,-1,sizeof(head));
scanf("%d",&N);
for(int i=1;i<=N;i++)scanf("%d",&a[i]);
scanf("%d",&M);
for(int i=0;i<M;i++)
{
int ta,tb; scanf("%d%d",&ta,&tb);add(ta,tb,(a[tb]-a[ta])*(a[tb]-a[ta])*(a[tb]-a[ta]));
}
scanf("%d",&Q);
cout<<"Case "<<_count<<":"<<endl;
for(int i=0;i<Q;i++)
{
spfa(1);//fflush(stdin);
int e;cin>>e;
if(dis[e]==inf||fh[e]==1||dis[e]<3)
{
printf("?\n");
}
else
{
printf("%d\n",dis[e]);
}
}
}
return 0;
}
实验
CSP 2016-04-3: 路径解析
这道题的题意很难懂,讲的很绕脑子,总之就是让你看不懂。其实大概是这么个意思:给你一个相对/绝对路径 但是这个相对/绝对路径含有"…"、".“和多余的”/"。然后让你把他们化成标准的形式:也就是把"…"、".“和多余的”/“消去。并把相对化为绝对路径。
首先把相对路径化为绝对路径,我们只需要在相对路径前面加上输入中输入的当前目录,可以用string的+直接完成。
然后我们就用Vector存储字符串,用getline一次读取一行,在存储的时候把” / “消掉只存储路径字符串,输出的时候再加上。
然后判断是否是第一个” / “,消掉多余的” / “。然后消掉 " … " 和” . "即可。可以用vector的erase函数删除路径串。
#include<bits/stdc++.h>
using namespace std;
int n;
string now,t;
vector<string> vec[20];
void out(){
for(int i=0;i<n;i++)
{
if(vec[i].size()==0)
{
cout << "/" ;
}
else{
for(int j=0;j<vec[i].size();j++)
{
cout << "/" ;
if(j<vec[i].size())
{
cout << vec[i][j];
}
}
}
cout << endl;
}
}
int main(){
cin>>n;cin>>now;
cin.ignore();
for(int i=0;i<n;i++)
{t.clear();
getline(cin,t);
if(t[0]!='/')
{
t=now+"/"+t;
}
string temp;
temp="";bool op=true;
for(int j=1;j<=t.size();j++)
{
if(t[j]=='/'||j==t.size())
{
if(op==false)
{
op=true;
vec[i].push_back(temp);
temp.clear();
}
else if(op==true){
continue;
}
}
else{
op=false;
temp=temp+t[j];
}
}
}
for(int i=0;i<n;i++)
{
int j=0;
while(j<vec[i].size())
{
if(vec[i][j]=="..")
{
if(j==0)
{
vec[i].erase(vec[i].begin());
}
else{
j--;
vec[i].erase(vec[i].begin()+j);
vec[i].erase(vec[i].begin()+j);
}
}
else if(vec[i][j]==".")
{
vec[i].erase(vec[i].begin()+j);
}
else j++;
}
}
out();
return 0;
}