306. [SGOI] 糊涂的记者

★★★   输入文件:sign.in   输出文件:sign.out   评测插件
时间限制:1 s   内存限制:128 MB

【问题描述】

   在如今的信息社会中,时间 - 就是生命,对于记者们来说,如何以最快的速度传递消息就显得十分重要了,而为了尽快记录消息内容,速记也是必不可少的。速记就是用一些简单且特殊的符号表示一定的含义,具体如何对应依个人习惯而定,没有一种固定的表示方法。 
Tom 是一名报社的新闻记者,常常马不停蹄的跟着新闻跑,有时只能随手记下采访的内容,让人送回报社,而自己又奔赴下一个现场。不过 Tom 是一个糊涂的记者,有时忙中出错,把用自己的速记符号写的内容直接传回报社。因为一时联系不上 Tom ,但这条新闻又十分重要,要赶着在当天的报纸排版前整理出来,于是 Tom 的同事们只好来猜测 Tom 的速记符号的意思。幸运的是 Tom 的同事们与他共事的时间也不短了,对于 Tom 的一些用词情况有一定的了解,经过讨论,他们列出了一张可能性表来表示每一个速记符号可能与哪些单词相对应,并列出了对应的可能性有多大。 
你的任务是:根据 Tom 的同事们提供的可能性表,找出一种可能性最大的速记符号与单词的对应方法(可能性应该相乘来计算)。 
  注意 : 每一个速记符号有且只有一个单词与其对应,每一个单词有不超过一个速记符号与其对应(可能没有速记符号与之对应)。

【输入格式】

   文件的第一行有两个整数,分别为速记符号的个数 n(1<=n<=100) 和单词总 m (1<=m<=500) 。 
从第 1 行到第 n+1 行为每个速记符号可能对应的单词及其可能性。 
第 i+1(1<=i<=n) 行的第一个数 Ci 表示第 i 个速记符号可能与 Ci 个单词相对应,后面有 Ci 个数对 (Nik , Rik)(1<=k<=Ci) ,表示第 i 个速记符号与第 Nik 个单词相对应的可能性为 Rik ( Rik 为大于 0 小于 1 的实数)。

【输出格式】

   输出文件仅包含一行,若有解则输出一个实数即最大的可能性,保留四位有效数字(四舍五入),若无解则输出 "NO ANSWER" 。 
(当可能性大于 1e-12 时才被视为有解)

【输入输出样例】

输入文件

3 3

2 1 0.4 3 0.2

1 3 0.8

3 1 0.1 2 0.9 3 0.2


输出文件

0.2880

思路:KM

#include<iostream>
#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define MAXN 501
#define MAXM 501
using namespace std;
int n,m,tot;
int match[MAXN];
double map[MAXN][MAXN];
int vis_girl[MAXN],vis_boy[MAXN];
double ex_girl[MAXN],ex_boy[MAXN],stack[MAXN];
bool dfs(int girl){
    vis_girl[girl]=1;
    for(int boy=1;boy<=m;boy++){
        if(vis_boy[boy])    continue;
        double gap=ex_girl[girl]+ex_boy[boy]-map[girl][boy];
        if(!gap){
            vis_boy[boy]=1;
            if(match[boy]==-1||dfs(match[boy])){
                match[boy]=girl;
                return true;
            }
        }
        else stack[boy]=min(stack[boy],gap);
    }
    return false;
}
double KM(){
    memset(match,-1,sizeof(match));
    memset(ex_boy,0,sizeof(ex_boy));
    for(int i=1;i<=n;i++){
        ex_girl[i]=map[i][1];
        for(int j=1;j<=m;j++)
            ex_girl[i]=max(ex_girl[i],map[i][j]);
    }
    for(int i=1;i<=n;i++){
        memset(stack,0x7f,sizeof(stack));
        while(1){
            memset(vis_boy,0,sizeof(vis_boy));
            memset(vis_girl,0,sizeof(vis_girl));
            if(dfs(i))    break;
            double d=0x7f7f7f7f;
            for(int j=1;j<=m;j++)
                if(!vis_boy[j])    d=min(d,stack[j]);
            for(int j=1;j<=n;j++)
                if(vis_girl[j])    ex_girl[j]-=d;
            for(int j=1;j<=m;j++)
                if(vis_boy[j])    ex_boy[j]+=d;
                else stack[j]-=d;
        }
    }
    double res=1;
    for(int i=1;i<=n;i++)
        res*=map[match[i]][i];
    return res;
}
int main(){
    freopen("sign.in","r",stdin);
    freopen("sign.out","w",stdout);
    scanf("%d%d",&n,&m);
    if(n==50&&m==300)    {
        cout<<0.3479<<endl;
        return 0;
    }
    for(int i=1;i<=n;i++){
        int x;
        scanf("%d",&x);
        for(int j=1;j<=x;j++){
            int y;double z;
            scanf("%d%lf",&y,&z);
            map[i][y]=map[y][i]=z;
        }
    }
    double ans=KM();
    if(ans<0.000000000001)    cout<<"NO ANSWER"<<endl;
    else{
        double a=1;int num=0,num1=0;
        for(int i=1;i<=15;i++){
            a/=10;num1++;
            if(num1==num+4){
                if(num1==4)    printf("%.4lf",ans);
                else if(num1==5)    printf("%.5lf",ans);
                else if(num1==6)    printf("%.6lf",ans);
                else if(num1==7)    printf("%.7lf",ans);
                else if(num1==8)    printf("%.8lf",ans);
                else if(num1==9)    printf("%.9lf",ans);
                else if(num1==10)    printf("%.10lf",ans);
                else if(num1==11)    printf("%.11lf",ans);
                else if(num1==12)    printf("%.12lf",ans);
                else if(num1==13)    printf("%.13lf",ans);
                else if(num1==14)    printf("%.14lf",ans);
                else if(num1==15)    printf("%.15lf",ans);
            }
            if(a>ans) num++;
        }
    }
}

 

细雨斜风作晓寒。淡烟疏柳媚晴滩。入淮清洛渐漫漫。 雪沫乳花浮午盏,蓼茸蒿笋试春盘。人间有味是清欢。