1. 程式人生 > >[fzu 2271]不改變任意兩點最短路至多刪的邊數

[fzu 2271]不改變任意兩點最短路至多刪的邊數

cnblogs 矛盾 targe span 鏈接 clu i++ 循環 沒有

題目鏈接:http://acm.fzu.edu.cn/problem.php?pid=2271

題目中說每條邊的邊權都是[1,10]之間的整數,這個條件非常關鍵!以後一定要好好讀題啊……

做10次循環,第i次循環加邊權為i的邊,如果這條邊小於當前兩點間最短路,就加邊,更新兩點距離;否則就不要這個邊。

每次循環過後,做一次Floyd。至多做10次Floyd。

有一個猜想,比賽的時候就想到了:從小到大加邊,如果這個邊比這兩點之間的最短距離小,就要,否則就不要。這個猜想不會證……

但是只想到了每次更改距離以後都做一次Floyd,沒有想到一塊加權值相同的邊,然後再做Floyd。這裏就假設上面的猜想是正確的,然後證明一下統一做Floyd的方法也是正確的吧。

假設dis[][]維護著兩點間的最短距離。

首先,對於未經優化的方法,如果有一條邊加入了,說明這條邊的權比兩點的最短路短,如果沒有隨時維護,只維護到了上次權值不同的最後一條邊,由於dis隨著維護是越來越小的,所以現在的dis也顯然大於這個邊權,因此對於沒有優化的方法,這條邊會加進去。

然後,對於未經優化的方法,如果有一條邊沒有加入,說明這條邊的權w不比兩點的最短路短,如果沒有隨時維護,只維護到了上次權值不同的最後一條邊,由於用[1,w-1]的邊構成的最短路已經得到,假設這條權值是w的邊在優化後會加入,也就是說當前的dis>w,而優化以前沒有加入,說明dis‘<=w,所以意思就是,加入了一些權值為w的邊以後,dis變得<=w了,那這個dis‘只能是=w了。既然是權值為w的最短路,要麽是用權值為w的邊得到的,那此時dis應該也=w了,因為每遇到一個w的邊都會更新距離。要麽是用[1,w-1]的邊構成的,那這個在[1,w-1]之後就應該已經維護出來了。這兩種情況都與dis>w矛盾。所以假設失敗,這條邊在優化以後還是不會加入。

所以加不加入在優化前後是一樣的。

#include<cstdio>
#include<cstring>

const int maxn=105;
const int maxm=40005;
const int INF=0x3f3f3f3f;

struct Edge
{
    int u,v,w;
}edge[maxm];
int dis[maxn][maxn];

int main()
{
    int t;
    scanf("%d",&t);
    for (int cas=1;cas<=t;cas++)
    {
        int n,m;
        scanf(
"%d%d",&n,&m); for (int i=0;i<m;i++) { scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].w); } memset(dis,INF,sizeof(dis)); for (int i=1;i<=n;i++) dis[i][i]=0; int ans=0; for (int z=1;z<=10;z++) { for (int e=0;e<m;e++) { if (edge[e].w==z) { int u=edge[e].u; int v=edge[e].v; if (z<dis[u][v]) { dis[u][v]=z; dis[v][u]=z; ans++; } } } for (int k=1;k<=n;k++) for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) if (dis[i][k]+dis[k][j]<dis[i][j]) dis[i][j]=dis[i][k]+dis[k][j]; } printf("Case %d: %d\n",cas,m-ans); } return 0; }

[fzu 2271]不改變任意兩點最短路至多刪的邊數