1. 程式人生 > >洛谷 P3705 [SDOI2017]新生舞會 解題報告

洛谷 P3705 [SDOI2017]新生舞會 解題報告

做的 () set return 假設 else space 女生 AD

P3705 [SDOI2017]新生舞會

題目描述

學校組織了一次新生舞會,\(Cathy\)作為經驗豐富的老學姐,負責為同學們安排舞伴。

\(n\)個男生和\(n\)個女生參加舞會買一個男生和一個女生一起跳舞,互為舞伴。

\(Cathy\)收集了這些同學之間的關系,比如兩個人之前認識沒計算得出\(a_{i,j}\)

\(Cathy\)還需要考慮兩個人一起跳舞是否方便,比如身高體重差別會不會太大,計算得出\(b_{i,j}\) ,表示第\(i\)個男生和第\(j\)個女生一起跳舞時的不協調程度。

當然,還需要考慮很多其他問題。

\(Cathy\)想先用一個程序通過\(a_{i,j}\)\(b_{i,j}\)

,求出一種方案,再手動對方案進行微調。

\(Cathy\)找到你,希望你幫她寫那個程序。

一個方案中有\(n\)對舞伴,假設沒對舞伴的喜悅程度分別是\(a‘_1,a‘_2,...,a‘_n\)
假設每對舞伴的不協調程度分別是\(b‘_1,b‘_2,...,b‘_n\)
\(C=\frac{a‘_1+a‘_2+...+a‘_n}{b‘_1+b‘_2+...+b‘_n}\)
\(Cathy\)希望\(C\)值最大。

輸入輸出格式

輸入格式:

第一行一個整數\(n\)
接下來\(n\)行,每行\(n\)個整數,第\(i\)行第\(j\)個數表示\(a_{i,j}\)
接下來\(n\)行,每行\(n\)

個整數,第i行第j個數表示\(b_{i,j}\)

輸出格式:

一行一個數,表示\(C\)的最大值。四舍五入保留6位小數,選手輸出的小數需要與標準輸出相等。

說明

對於10%的數據, \(1≤n≤5\)

對於40%的數據, \(1≤n≤18\)

另有20%的數據, \(b_{i,j}≤1\)

對於100%的數據,\(a_{i,j},b_{i,j}<=10^4,1≤n≤100\)


做的第一道01分數規劃的題。

01分數規劃問題大多采用一種二分答案解法。

我們分數拆成整數,即
\(C*(b‘_1+b‘_2+...+b‘_n)=a‘_1+a‘_2+...+a‘_n\)

然後猜測一個\(C\)的值,檢驗在最優\(a,b\)

配對的情況下左邊與右邊的大小關系,進而對\(C\)進行二分答案

如何求解最優配對呢?

再轉化一下式子,左邊-右邊=
\(\sum_{i=1}^n (a‘_i-b‘_i*C)\)

我們要求解sum的最大值。

如果我們把對應點連上邊\((a‘_i-b‘_i*C)\),就變成了跑 最大費用最大流或者 二分圖帶權匹配了

最後一點,這個題它卡常...


吸氧code:

#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
const int N=204;
const int inf=0x3f3f3f3f;
const double finf=1e100;
int a[N][N],b[N][N],n,used[N],pre[N];
double l=0.0,r=0.0,dis[N];
struct node
{
    double c;
    int from,to,next,w;
}edge[N*N];
int head[N],cnt=-1;
void add(int u,int v,double c,int w)
{
    edge[++cnt].to=v;edge[cnt].from=u;edge[cnt].w=w;edge[cnt].c=c;edge[cnt].next=head[u];head[u]=cnt;
    edge[++cnt].to=u;edge[cnt].from=v;edge[cnt].w=0;edge[cnt].c=-c;edge[cnt].next=head[v];head[v]=cnt;
}
queue <int > q;
bool spfa()
{
    memset(used,0,sizeof(used));
    memset(pre,0,sizeof(pre));
    while(!q.empty()) q.pop();
    for(int i=1;i<=2*n+1;i++) dis[i]=-finf;
    dis[0]=0;
    pre[0]=-1;
    used[0]=1;
    q.push(0);
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        used[u]=0;
        for(int i=head[u];i!=-1;i=edge[i].next)
        {
            int v=edge[i].to,w=edge[i].w;double c=edge[i].c;
            if(w&&dis[u]+c>dis[v])
            {
                dis[v]=dis[u]+c;
                pre[v]=i;
                if(!used[v])
                {
                    used[v]=1;
                    q.push(v);
                }
            }
        }
    }
    return dis[2*n+1]!=-finf;
}
bool check(double L)
{
    memset(head,-1,sizeof(head));
    cnt=-1;
    for(int i=1;i<=n;i++)
    {
        add(0,i,0,1);
        add(i+n,2*n+1,0,1);
    }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            add(i,j+n,double(a[i][j])-double(b[i][j])*L,1);
    double sum=0.0;
    while(spfa())
    {
        int now=2*n+1;
        while(pre[now]!=-1)
        {
            edge[pre[now]].w-=1;
            edge[pre[now]^1].w+=1;
            sum+=edge[pre[now]].c;
            now=edge[pre[now]].from;
        }
    }
    return sum<0.0;
}

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
        {
            scanf("%d",&a[i][j]);
            r+=a[i][j];
        }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            scanf("%d",&b[i][j]);
    while(l+1e-7<r)
    {
        double mid=(l+r)/2.0;
        if(check(mid))
            r=mid;
        else
            l=mid;
    }
    printf("%lf\n",l);
    return 0;
}

2018.6.4

洛谷 P3705 [SDOI2017]新生舞會 解題報告