Codeforces 507E Breaking Good

Breaking Good is a new video game which a lot of gamers want to have. There is a certain level in the game that is really difficult even for experienced gamers.

Walter William, the main character of the game, wants to join a gang called Los Hermanos (The Brothers). The gang controls the whole country which consists of n cities with m bidirectional roads connecting them. There is no road is connecting a city to itself and for any two cities there is at most one road between them. The country is connected, in the other words, it is possible to reach any city from any other city using the given roads.
The roads aren’t all working. There are some roads which need some more work to be performed to be completely functioning.
The gang is going to rob a bank! The bank is located in city 1. As usual, the hardest part is to escape to their headquarters where the police can’t get them. The gang’s headquarters is in city n. To gain the gang’s trust, Walter is in charge of this operation, so he came up with a smart plan.
First of all the path which they are going to use on their way back from city 1 to their headquarters n must be as short as possible, since it is important to finish operation as fast as possible.

Then, gang has to blow up all other roads in country that don’t lay on this path, in order to prevent any police reinforcements. In case of non-working road, they don’t have to blow up it as it is already malfunctional.

If the chosen path has some roads that doesn’t work they’ll have to repair those roads before the operation.
Walter discovered that there was a lot of paths that satisfied the condition of being shortest possible so he decided to choose among them a path that minimizes the total number of affected roads (both roads that have to be blown up and roads to be repaired).
Can you help Walter complete his task and gain the gang’s trust?
Input

The first line of input contains two integers n, m (2 ≤ n ≤ 105, ), the number of cities and number of roads respectively.

In following m lines there are descriptions of roads. Each description consists of three integers x, y, z (1 ≤ x, y ≤ n, ) meaning that there is a road connecting cities number x and y. If z = 1, this road is working, otherwise it is not.
Output

In the first line output one integer k, the minimum possible number of roads affected by gang.
In the following k lines output three integers describing roads that should be affected. Each line should contain three integers x, y, z (1 ≤ x, y ≤ n, ), cities connected by a road and the new state of a road. z = 1 indicates that the road between cities x and y should be repaired and z = 0 means that road should be blown up.
You may output roads in any order. Each affected road should appear exactly once. You may output cities connected by a single road in any order. If you output a road, it’s original state should be different from z.

After performing all operations accroding to your plan, there should remain working only roads lying on some certain shortest past between city 1 and n.
If there are multiple optimal answers output any.
Examples
Input

2 1
1 2 0

Output

1
1 2 1

Input

4 4
1 2 1
1 3 0
2 3 1
3 4 1

Output

3
1 2 0
1 3 1
2 3 0

Input

8 9
1 2 0
8 3 0
2 3 1
1 4 1
8 7 0
1 5 1
4 6 1
5 7 0
6 8 0

Output

3
2 3 0
1 5 0
6 8 1

Note

In the first test the only path is 1 - 2

In the second test the only shortest path is 1 - 3 - 4

In the third test there are multiple shortest paths but the optimal is 1 - 4 - 6 - 8
这题实在是想不到…
看了好几个题解…
题目大意:

给你N个点,M条无向边,这M条无向边中,0表示这条边需要维修才能走过去,1表示这条边不用维修。

现在需要从1走到N.并且希望路径最短。

此时需要选择一条路径,使得从1到N路径最短。

如果有多条最短路,希望能够使得花费最小,花费==(属于从1到n的最短路径中0的个数)+(不属于从1到n的最短路径1的个数);

对应输出需要变化的边的个数,以及变化的详情。

思路:

1、首先我们跑出从1到其他各点的单源最短路是没问题的。

此时得到dist【i】,表示从1到i的最短路径长度。

2、那么接下来考虑dp,那么我们设定dp【i】表示从1走到i点的最小花费【Min(属于从1到n的最短路径中0的个数)+(不属于从1到n的最短路径1的个数)】。

那么我们初始设定所有边都是0的话,那么初始化dp【1】=Σzi(原图中1的个数);

状态转移有(必须要保证有dist【v】==dist【u】+1,我们Dp的过程需要在最短路的基础上进行维护):

dp【v】=dp【u】-1(wi==1)

dp【v】=dp【u】+1(wi==0)

同时记录pre【i】,表示dp【i】的最优上一状态是pre【i】;

那么记录路径,因为题目保证没有重边,那么我们直接维护一下哪些边属于最短路+Dp路径上的即可。

那么对于输入进来的M条边,属于这些路径上的0需要变成1.1不动,那么不属于这些路径上的1需要变成0.同理0不动。

3、注意细节,注意初始化即可。
代码:

#include<stdio.h>
#include<string.h>
#include<vector>
#include<queue>
#include<map>
using namespace std;
struct node
{
    int from;
    int to;
    int w;
    int next;
}e[1050000];
int xx[105000];
int yy[105000];
int zz[105000];
vector<int >mp[105000];
int vis[105000];
int dist[105000];
int dp[105000];
int pre[105000];
int head[105000];
int cont;
int n,m,sum;
map<int ,int >ss;
void add(int from,int to,int w)
{
    e[cont].to=to;
    e[cont].w=w;
    e[cont].next=head[from];
    head[from]=cont++;
}
void SPFA()
{
    for(int i=1;i<=n;i++)dist[i]=0x3f3f3f3f;
    memset(vis,0,sizeof(vis));
    vis[1]=1;
    dist[1]=0;
    queue<int >s;
    s.push(1);
    while(!s.empty())
    {
        int u=s.front();
        s.pop();
        vis[u]=0;
        for(int i=0;i<mp[u].size();i++)
        {
            int v=mp[u][i];
            if(dist[v]>dist[u]+1)
            {
                dist[v]=dist[u]+1;
                if(vis[v]==0)
                {
                    vis[v]=1;
                    s.push(v);
                }
            }
        }
    }
}
void Dp()
{
    memset(pre,-1,sizeof(pre));
    for(int i=1;i<=n;i++)dp[i]=0x3f3f3f3f;
    dp[1]=sum;
    queue<int >s;
    s.push(1);
    while(!s.empty())
    {
        int u=s.front();
        s.pop();
        for(int i=head[u];i!=-1;i=e[i].next)
        {
            int v=e[i].to;
            int w=e[i].w;
            if(dist[v]==dist[u]+1)
            {
                if(w==1)
                {
                    if(dp[u]-1<dp[v])
                    {
                        dp[v]=dp[u]-1;
                        pre[v]=u;
                    }
                }
                if(w==0)
                {
                    if(dp[u]+1<dp[v])
                    {
                        dp[v]=dp[u]+1;
                        pre[v]=u;
                    }
                }
                s.push(v);
            }
        }
    }
    printf("%d\n",dp[n]);
    while(n>0)
    {
        ss[pre[n]]=n;
        n=pre[n];
    }
}
int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        sum=0;
        cont=0;
        ss.clear();
        memset(head,-1,sizeof(head));
        for(int i=1;i<=n;i++)mp[i].clear();
        for(int i=0;i<m;i++)
        {
            int x,y,z;
            scanf("%d%d%d",&x,&y,&z);
            mp[x].push_back(y);
            mp[y].push_back(x);
            add(x,y,z);
            add(y,x,z);
            sum+=z;
            xx[i]=x;yy[i]=y;zz[i]=z;
        }
        SPFA();
        Dp();
        for(int i=0;i<m;i++)
        {
            if(zz[i]==0)
            {
                if(ss[xx[i]]==yy[i]||ss[yy[i]]==xx[i])
                {
                    printf("%d %d 1\n",xx[i],yy[i]);
                }
            }
            if(zz[i]==1)
            {
                if(ss[xx[i]]==yy[i]||ss[yy[i]]==xx[i])continue;
                else
                {
                    printf("%d %d 0\n",xx[i],yy[i]);
                }
            }
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值