题意:

n个城市m条道路, 任务是在城市1和城市n之间选择一个最短路径,当有多个最短路径的时候选择影响值最小的(被摧毁的和修好的路的数目总数最少) ,道路分已修复和未修复两种状态,在选择好最短路径后,要修复好最短路径上未工作的路,并破坏其他路径上工作的路径。

题解:

修复 = 最短路的长度 - 最短路上修好的路
破坏的道路 = 整张图上修好的路 - 最短路径上修好的路
影响值= 修复+破化的道路
= 最短路上没修好的路+非最短路上修好的路
= 最短路的长度 - 最短路上修好的路 + 整张图上修好的路 - 最短路上修好的路
= 最短路的长度 + 整张图上修好的路 - 2 * 最短路上修好的路

所以要使影响值最小应该使得最短路径上面工作的道路最多
先spfa得到最短路
如果最短路相同,再判断路上修好的路的个数(在spfa的过程中记录)
根据公式得到影响值,影响值也就是题目要求输出的k
用pre实现路径存储,输出最短路上未修好的路
bool bo[]记录最短路径上哪些点出现过

代码:

代码内详细注释

#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
inline int read(){
   int s=0,w=1;
   char ch=getchar();
   while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
   while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();//s=(s<<3)+(s<<1)+(ch^48);
   return s*w;
}
int INF= 0x3f3f3f3f;
const int maxn=1e5+9;
struct node{
    int u,v,w;
}a[maxn];
//int vis[maxn];
int n,m;
int tot=0;//求路径中已修好的数量
pair<int,int>dis[maxn],pre[maxn];
bool vis[maxn],bo[maxn];
vector<pair<int,int> > edge[maxn];//存路径
queue<int>q;
void spfa()
{
    for(int i=1;i<=n;i++)dis[i]=make_pair(INF,0);
    dis[1].first=0;
    q.push(1);
    vis[1]=1;
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        vis[u]=0;
        for(pair<int,int> tmp:edge[u])
        {
            int v=tmp.first;
            int w=a[tmp.second].w;
            bool xx=((dis[v].first==dis[u].first+1)&&(dis[v].second<dis[u].second+w));
            //如果都是最短路,那就要已修好道路多的 
            if(dis[v].first>dis[u].first+1||xx)
            {
                dis[v].first=dis[u].first+1;
                dis[v].second=dis[u].second+w;
                pre[v]=make_pair(u,tmp.second);//存入上一个点以及编号
                if(!vis[v])
                {
                    vis[v]=1;
                    q.push(v); 
                }
            }

        }
    }
}
int main()
{
    cin>>n>>m;
    for(int i=1;i<=m;i++){
        cin>>a[i].u>>a[i].v>>a[i].w;
        edge[a[i].u].push_back(make_pair(a[i].v,i));//存下路径和序号 
        edge[a[i].v].push_back(make_pair(a[i].u,i));
        tot+=a[i].w;
    }
    spfa();
    int k;
    k=dis[n].first+tot-2*dis[n].second;
        //dis[n].first表示最短路的长度 
    // dis[n].second表示最短路上已修好的路 
    cout<<k<<endl;
    int now=n;
    while(now!=1)
    {
        int temp=pre[now].second;//上一个点的编号 
        if(a[temp].w==0)
        cout<<a[temp].u<<" "<<a[temp].v<<" "<<1<<endl;
        bo[temp]=1;//标记最短路上的路径 
        now=pre[now].first;
    }
    for(int i=1;i<=m;i++)
    {
        if(!bo[i]&&a[i].w==1)//如果不是最短路上的&&路是修好的
        cout<<a[i].u<<" "<<a[i].v<<" "<<0<<endl; 
    }
    return 0;
}