菲律宾海战
这个题没有什么可写的部分分吧
比较套路的一个题
两种其实差不多的解法
1,DP最短路
回顾最短路的实现中,我们用len[i]表示从起点到i这个点的最短距离
那么加入减小边权了呢,我们可以用len[i][j]表示到第i个点,用掉k次减少边权机会的最小距离
转移是显然的,在普通最短路转移的基础上,加入从用掉j个支援到用j+1个的转移,
len[v][j+1]=min(len[v][j+1],len[u][j]+dis[u][v]/2)
最后取min(len[n][i])(i属于{[1,k]})即可
#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
using namespace std;
const int maxn=60;
int n,m,ki,len[maxn][maxn],e[maxn][maxn];
bool exist[maxn][maxn];
int spfa()
{
queue<int>q,qs;
memset(len,0x3f,sizeof(len));
len[1][0]=0;
exist[1][0]=1;
q.push(1),qs.push(0);
while(!q.empty())
{
int i=q.front(),k=qs.front();
q.pop(),qs.pop();
exist[i][k]=0;
for(int j=1;j<=n;j++)
{
if(e[i][j]>1e6)
continue;
if(len[j][k]>len[i][k]+e[i][j])
{
len[j][k]=len[i][k]+e[i][j];
if(!exist[j][k])
exist[j][k]=1,q.push(j),qs.push(k);
}
if(len[j][k+1]>len[i][k]+e[i][j]/2)
{
len[j][k+1]=len[i][k]+e[i][j]/2;
if(!exist[j][k+1]&&k<ki)
exist[j][k+1]=1,q.push(j),qs.push(k+1);
}
}
}
int ret=1e9;
for(int i=0;i<=ki;i++)
ret=min(ret,len[n][i]);
return ret;
}
int main()
{
scanf("%d%d%d",&n,&m,&ki);
memset(e,0x3f,sizeof(e));
for(int x,y,d,i=1;i<=m;i++)
{
scanf("%d%d%d",&x,&y,&d);
e[x][y]=e[y][x]=d;
}
printf("%d\n",spfa());
return 0;
}
2,分层图最短路
本来是有n个点的,但是不好处理,我们不妨把每个点拆成k+1个,第j个表示到这个点要用j次支援
建图的时候同层正常连边,本层到上层连边时边权除2,直接跑最短路,统计答案时枚举第n个点各层最小代价即可
#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
using namespace std;
const int maxn=2600;
struct edge{
int next,to,dis;
}e[210010];
int cnt,head[maxn],n,m,ki,len[maxn];
bool exist[maxn];
inline void add(int x,int y,int d)
{
e[++cnt].next=head[x];
e[cnt].to=y;
e[cnt].dis=d;
head[x]=cnt;
}
int spfa()
{
queue<int> q;
memset(len,0x3f,sizeof(len));
q.push(1);
len[1]=0;
exist[1]=1;
while(!q.empty())
{
int u=q.front();
q.pop();
exist[u]=0;
for(int v,i=head[u];i;i=e[i].next)
if(len[v=e[i].to]>len[u]+e[i].dis)
{
len[v]=len[u]+e[i].dis;
if(!exist[v])
exist[v]=1,q.push(v);
}
}
int ret=1e9;
for(int i=1;i<=ki+1;i++)
ret=min(ret,len[n*i]);
return ret;
}
int main()
{
scanf("%d%d%d",&n,&m,&ki);
memset(e,0x3f,sizeof(e));
for(int x,y,d,i=1;i<=m;i++)
{
scanf("%d%d%d",&x,&y,&d);
for(int j=0;j<=ki;j++)
{
add(x+j*n,y+j*n,d),add(y+j*n,x+j*n,d);
if(j<ki)
add(x+j*n,y+(j+1)*n,d/2),add(y+j*n,x+(j+1)*n,d/2);
}
}
printf("%d\n",spfa());
return 0;
}