Word Rings POJ2949 (Bellman-ford算法,找平均值最大的環)

題意本身比較簡單,就是找一個平均值最大的環。
在做這個題之前,我們先解決一個問題,如何找到一個平均值最小的環。

設有一個環中,有k 個點,那麼這個環的平均權值X是:
X=(w1+w2+w3+,..,,,wk)/k
然後我們把k乘過去:(w1+w2+…..wk)=kX

Bellman_ford算法的一個重要作用是判斷負圈,所以這個題可以利用這個特點來做。
再把(w1+w2+…..wk)=kX處理一下。

就是(w1-X)+(w2-X)+(w3-X)+(w4-X)+…+(wk-X)=0
把這個等號改成小於號:(w1-X)+(w2-X)+(w3-X)+(w4-X)+…+(wk-X)<0

所以我們首先二分這個X,把所有的邊權減去X,如果說有平均權值爲X的,必定會出現:
(w1-X)+(w2-X)+(w3-X)+(w4-X)+…+(wk-X)=0(w1…wk都是那個環裏的點)
所以讓這個X稍微大一點點,就會出現負圈了。
由於如果X很大很大的話,也可以產生這個現象。
所以我們要找的X,就是那個剛好使得有這個式子產生的,最小的這個值。
這個值就是 最小的環的平均值(確切的說比平均值稍微大一點點,這裏是精度問題了)。

這個題不一樣的地方在於,是求最大的,所以我們需要改變一下。
把圖中所有的邊改成C-w(其中C是一個大於所有w的數字,注意不要溢出)。

再在這個新圖裏面去找最小的平均值。

就變成了 (C-w1-X)+(C-w2-X)+(C-w3-X)+(C-w4-X)+…+(C-wk-X)=0
然後把這個式子變成了
C-X=(w1+w2.+w3+…+wk)/K
C-X就是新圖裏最小的平均值。
如果要C-X越小,就意味着X要越大。
所以C-X最小,就意味着X最大。

這個題就這樣做出來了。

細節:
①double不可以直接比大小,要寫一個eps量,這裏我寫了1e-3, 連1e-2都會WA的。。
②所有單詞,前兩個字母所表示的點的dis都是0,然後再跑Bellman-ford。

#include<iostream>
#include<string.h>
#include<string>
#include<algorithm>
#include<stdio.h>
#include<map>
#include<vector>
using namespace std;
double eps=1e-3;
const double alpha=1001;
bool ashead[120000];
double dis[120000];
map<string,int> mp;
struct Edge
{
    int from,to;
    double w;
};
vector<Edge> E;
int n;
char input[1200];

void init()
{
    memset(ashead,0,sizeof(ashead));
    mp.clear();
    E.clear();
}

bool Bellman_ford(double mid)
{
    for(int i=0;i<120000;i++) dis[i]=1e9;
    int size=E.size();
    dis[0]=0;
    for(int i=0;i<size;i++)
    {
        if(E[i].from==0)
        {
            dis[E[i].to]=0;
            continue;
        }
        E[i].w-=mid;
        //cout<<E[i].w<<endl;
    }
    int cnt=mp.size();
    for(int i=1;i<=cnt;i++)
    {
        for(int j=0;j<size;j++)
        {
            int from=E[j].from;
            int to=E[j].to;
            double w=E[j].w;
            if(dis[to]-(dis[from]+w)>eps)
            {
                dis[to]=dis[from]+w;
                //cout<<dis[to]<<endl;
            }
        }
    }

    bool can=true;
    for(int j=0;j<size;j++)
    {
        int from=E[j].from;
        int to=E[j].to;
        double w=E[j].w;
        //cout<<dis[from]<<" "<<dis[to]<<" "<<w<<endl;
        if(dis[to]-(dis[from]+w)>eps)
        {
            //cout<<"fuck"<<endl;
            can=false;
        }
    }

    for(int i=0;i<size;i++)
    {
        if(E[i].from==0) continue;
        E[i].w+=mid;
    }
    return can;
}

int main()
{
    while(cin>>n)
    {
        if(n==0) break;
        init();
        int cnt=1;
        for(int i=1;i<=n;i++)
        {
            scanf("%s",input);
            int len=strlen(input);
            if(len<2) continue;//以防萬一吧。。。
            string temp1;
            temp1+=input[0];
            temp1+=input[1];

            if(mp[temp1]==0) mp[temp1]=cnt++;

            string temp2;
            //temp.clear();
            temp2+=input[len-2];
            temp2+=input[len-1];
            if(mp[temp2]==0) mp[temp2]=cnt++;

            Edge ts;
            ts.from=mp[temp1];
            ts.to=mp[temp2];
            ts.w=alpha-len;
            E.push_back(ts);


            if(ashead[mp[temp1]]==0)
            {
                Edge tt;
                tt.from=0;
                tt.to=mp[temp1];
                tt.w=0;
                E.push_back(tt);
                ashead[mp[temp1]]=1;
            }


        }

        double ans=0x3f3f3f3f;
        double l=0;
        double r=1200;
        //cout<<mp.size()<<endl;
        while(r-l>eps)
        {
            double mid=(l+r)/2;
            //cout<<mid<<endl;
            if(Bellman_ford(mid)==false)//有負圈
            {
                ans=min(mid,ans);
                r=mid;
            }
            else l=mid;
        }
        //cout<<ans<<endl;
        if(ans==0x3f3f3f3f) printf("No solution.\n");
        else
        {
            ans=alpha-ans;
            printf("%.2lf\n",ans);
        }

    }


    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章