2021.10.19
T1:寻找道路
Problem:
在有向图 G 中,每条边的长度均为 1,现给定起点和终点,请你在图中找一条从起点到终点的路径,该路径满足以下条件:
- 路径上的所有点的出边所指向的点都直接或间接与终点连通。
- 在满足条件1 的情况下使路径最短。
注意:图 G 中可能存在重边和自环,题目保证终点没有出边。
请你输出符合条件的路径的长度。
Code:
1 #include<bits/stdc++.h> 2 using namespace std; 3 int n,m,x,y,s,t,sum,cnt; 4 int head[20010],nxt[500000],to[500000],dis[20010]; 5 int nhead[20010],nnxt[500000],nto[500000],ans; 6 bool dvis[20010],fky[20010],vis[20010]; 7 queue<int>dl,js; 8 void cr(int x,int y){ 9 sum++; 10 nxt[sum]=head[x]; 11 head[x]=sum; 12 to[sum]=y; 13 } 14 void ncr(int x,int y){ 15 cnt++; 16 nnxt[cnt]=nhead[x]; 17 nhead[x]=cnt; 18 nto[cnt]=y; 19 } 20 void dfs(int x){ 21 for(int tp=nhead[x];tp;tp=nnxt[tp]){ 22 if(!dvis[nto[tp]]){ 23 dvis[nto[tp]]=true; 24 dfs(nto[tp]); 25 } 26 } 27 } 28 void ndfs(int x){ 29 for(int tp=nhead[x];tp;tp=nnxt[tp]){ 30 if(!fky[nto[tp]]){ 31 fky[nto[tp]]=true; 32 } 33 } 34 } 35 void bfs(int x){ 36 dl.push(x); 37 js.push(0); 38 vis[x]=true; 39 while(!dl.empty()){ 40 if(dl.front()==t){ 41 ans=js.front(); 42 return ; 43 } 44 for(int tp=head[dl.front()];tp;tp=nxt[tp]){ 45 if(!fky[to[tp]]&&!vis[to[tp]]){ 46 dl.push(to[tp]); 47 js.push(js.front()+1); 48 vis[to[tp]]=true; 49 } 50 } 51 dl.pop(); 52 js.pop(); 53 } 54 } 55 int main(){ 56 scanf("%d%d",&n,&m); 57 for(int i=1;i<=m;i++){ 58 scanf("%d%d",&x,&y); 59 cr(x,y); 60 ncr(y,x); 61 } 62 scanf("%d%d",&s,&t); 63 dvis[t]=true; 64 dfs(t); 65 for(int i=1;i<=n;i++){ 66 if(!dvis[i]) ndfs(i); 67 } 68 bfs(s); 69 if(ans||s==t) printf("%d\n",ans); 70 else printf("-1\n"); 71 return 0; 72 }
T2:宝藏
Problem:
参与考古挖掘的小明得到了一份藏宝图,藏宝图上标出了 n 个深埋在地下的宝藏屋, 也给出了这 n 个宝藏屋之间可供开发的 m 条道路和它们的长度。
小明决心亲自前往挖掘所有宝藏屋中的宝藏。但是,每个宝藏屋距离地面都很远,也就是说,从地面打通一条到某个宝藏屋的道路是很困难的,而开发宝藏屋之间的道路则相对容易很多。
小明的决心感动了考古挖掘的赞助商,赞助商决定免费赞助他打通一条从地面到某个宝藏屋的通道,通往哪个宝藏屋则由小明来决定。
在此基础上,小明还需要考虑如何开凿宝藏屋之间的道路。已经开凿出的道路可以 任意通行不消耗代价。每开凿出一条新道路,小明就会与考古队一起挖掘出由该条道路所能到达的宝藏屋的宝藏。另外,小明不想开发无用道路,即两个已经被挖掘过的宝藏屋之间的道路无需再开发。
新开发一条道路的代价是 L×K。其中 L代表这条道路的长度,K 代表从赞助商帮你打通的宝藏屋到这条道路起点的宝藏屋所经过的宝藏屋的数量(包括赞助商帮你打通的宝藏屋和这条道路起点的宝藏屋) 。
请你编写程序为小明选定由赞助商打通的宝藏屋和之后开凿的道路,使得工程总代价最小,并输出这个最小值。
Code:
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 int n,m,tot,ans; 5 int map[110][110],dis[110][110],Log[4100]; 6 int f[15][4100],g[4100],ref[4100],v[15],p[15]; 7 inline int min(const int &a,const int &b){ 8 return a<b?a:b; 9 } 10 int main(){ 11 scanf("%d%d",&n,&m); 12 int i,j,a,b,c,x; 13 for(i=0;i<n;i++) for(j=0;j<n;j++) map[i][j]=60000000; 14 for(i=1;i<=m;i++){ 15 scanf("%d%d%d",&a,&b,&c); 16 a--; 17 b--; 18 map[a][b]=map[b][a]=min(map[a][b],c); 19 } 20 for(i=0;i<n;i++) Log[1<<i]=i; 21 memset(f,0x3f,sizeof(f)); 22 for(i=0;i<n;i++) f[0][1<<i]=0; 23 for(i=0;i<n;i++){ 24 for(x=0;x<(1<<n);x++){ 25 tot=0; 26 for(a=0;a<n;a++){ 27 if(!(x&(1<<a))) 28 { 29 v[tot]=60000000; 30 p[tot]=1<<a; 31 for(j=x;j;j-=j&-j){ 32 b=Log[j&-j]; 33 v[tot]=min(v[tot],map[a][b]*(i+1)); 34 } 35 tot++; 36 } 37 } 38 for(j=1;j<(1<<tot);j++){ 39 g[j]=g[j-(j&-j)]+v[Log[j&-j]]; 40 ref[j]=ref[j-(j&-j)]|p[Log[j&-j]]; 41 f[i+1][x|ref[j]]=min(f[i+1][x|ref[j]],f[i][x]+g[j]); 42 } 43 } 44 } 45 ans=1<<30; 46 for(i=0;i<=n;i++) ans=min(ans,f[i][(1<<n)-1]); 47 printf("%d",ans); 48 return 0; 49 }
T3:换教室
Problem:
对于刚上大学的牛牛来说,他面临的第一个问题是如何根据实际情况申请合适的课程。
在可以选择的课程中,有 2n 节课程安排在 n个时间段上。在第 i(1≤i≤n)个时间段上,两节内容相同的课程同时在不同的地点进行,其中,牛牛预先被安排在教室 ci上课,而另一节课程在教室 di 进行。
在不提交任何申请的情况下,学生们需要按时间段的顺序依次完成所有的 n 节安排好的课程。如果学生想更换第 i 节课程的教室,则需要提出申请。若申请通过,学生就可以在第 i 个时间段去教室 di 上课,否则仍然在教室 ci 上课。
由于更换教室的需求太多,申请不一定能获得通过。通过计算,牛牛发现申请更换第 i 节课程的教室时,申请被通过的概率是一个已知的实数 ki,并且对于不同课程的申请,被通过的概率是互相独立的。
学校规定,所有的申请只能在学期开始前一次性提交,并且每个人只能选择至多 m 节课程进行申请。这意味着牛牛必须一次性决定是否申请更换每节课的教室,而不能根据某些课程的申请结果来决定其他课程是否申请;牛牛可以申请自己最希望更换教室的 m 门课程,也可以不用完这 m 个申请的机会,甚至可以一门课程都不申请。
因为不同的课程可能会被安排在不同的教室进行,所以牛牛需要利用课间时间从一间教室赶到另一间教室。
牛牛所在的大学有 v个教室,有 e 条道路。每条道路连接两间教室,并且是可以双向通行的。由于道路的长度和拥堵程度不同,通过不同的道路耗费的体力可能会有所不同。 当第 i(1≤i≤n−1)节课结束后,牛牛就会从这节课的教室出发,选择一条耗费体力最少的路径前往下一节课的教室。
现在牛牛想知道,申请哪几门课程可以使他因在教室间移动耗费的体力值的总和的期望值最小,请你帮他求出这个最小值。
Code:
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int MAXN=2005; 4 const int MAXM=305; 5 const double INF=1e9+7; 6 int ln,lm,n,m; 7 int c[MAXN]; 8 int d[MAXN]; 9 double k[MAXN]; 10 int G[MAXM][MAXM]; 11 double dp[MAXN][MAXN][2]; 12 void floyed(){ 13 for(int k=1;k<=n;k++){ 14 for(int i=1;i<=n;i++){ 15 for(int j=1;j<=n;j++){ 16 G[i][j]=min(G[i][j],G[i][k]+G[k][j]); 17 } 18 } 19 } 20 } 21 void solve(){ 22 for(int i=1;i<=ln;i++){ 23 for(int j=0;j<=lm;j++){ 24 dp[i][j][0]=dp[i][j][1]=INF; 25 } 26 } 27 dp[0][0][0]=0; 28 for(int i=1;i<=ln;i++){ 29 for(int j=0;j<=lm;j++){ 30 dp[i][j][0]=min(dp[i-1][j][0]+G[c[i-1]][c[i]],dp[i-1][j][1]+1.0*G[d[i-1]][c[i]]*k[i-1]+G[c[i-1]][c[i]]*(1-k[i-1])); 31 if(j>0){ 32 dp[i][j][1]=dp[i-1][j-1][0]+1.0*G[c[i-1]][d[i]]*k[i]+1.0*G[c[i-1]][c[i]]*(1-k[i]); 33 } 34 if(j>1){ 35 dp[i][j][1]=min(dp[i][j][1],dp[i-1][j-1][1]+k[i-1]*(G[d[i-1]][d[i]]*k[i]+G[d[i-1]][c[i]]*(1-k[i]))+(1-k[i-1])*(G[c[i-1]][d[i]]*k[i]+G[c[i-1]][c[i]]*(1-k[i]))); 36 } 37 } 38 } 39 } 40 int main(){ 41 scanf("%d%d%d%d",&ln,&lm,&n,&m); 42 for(int i=1;i<=ln;i++){ 43 scanf("%d",c+i); 44 } 45 for(int i=1;i<=ln;i++){ 46 scanf("%d",d+i); 47 } 48 for(int i=1;i<=ln;i++){ 49 scanf("%lf",k+i); 50 } 51 for(int i=1;i<=n;i++){ 52 for(int j=i+1;j<=n;j++){ 53 G[i][j]=G[j][i]=INF; 54 } 55 } 56 for(int i=1;i<=m;i++){ 57 int u,v,w; 58 scanf("%d%d%d",&u,&v,&w); 59 if(u!=v){ 60 G[u][v]=G[v][u]=min(G[u][v],w); 61 } 62 } 63 floyed(); 64 solve(); 65 double ans=INF; 66 for(int j=0;j<=lm;j++){ 67 ans=min(ans,min(dp[ln][j][0],dp[ln][j][1])); 68 } 69 printf("%.2f",ans); 70 return 0; 71 }