新生舞会

 

 

/*
    01分数规划,二分答案k,看是否存在方案使的(a1+a2+a3...)/(b1+b2+b3...)>=k。
    即(a1-k*b1)+(a2-k*b2)+(a3-k*b3)...>=0,那么用费用流写就行了。
    需要注意的是常数问题没需要用一点黑科技,比如把结构体改成数组。 
*/
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#define N 210
#define M 40100
#define eps 0.0000001
#define inf 1000000000
using namespace std;
int a[N][N],b[N][N],n;
int head[N],inq[N],fa[N],S,T,cnt=1;
int to[M],f[M],pre[M];
double dis[N],w[M];
queue<int> q;
void add(int u,int v,int ff,double ww){
    to[++cnt]=v;pre[cnt]=head[u];f[cnt]=ff;w[cnt]=ww;head[u]=cnt;
    to[++cnt]=u;pre[cnt]=head[v];f[cnt]=0;w[cnt]=-ww;head[v]=cnt;
}
bool spfa(){
    for(int i=0;i<=T;i++) dis[i]=-inf;
    dis[S]=0;q.push(S);
    while(!q.empty()){
        int u=q.front();q.pop();inq[u]=0;
        for(int i=head[u];i;i=pre[i])
            if(f[i]>0&&dis[to[i]]<dis[u]+w[i]){
                dis[to[i]]=dis[u]+w[i];
                fa[to[i]]=i;
                if(!inq[to[i]]) q.push(to[i]),inq[to[i]]=1;
            }
    }
    return dis[T]!=-inf;
}
double up_data(){
    int tmp=fa[T];
    while(tmp){
        f[tmp]--;
        f[tmp^1]++;
        tmp=fa[to[tmp^1]];
    }
    return dis[T];
}
double work(double k){
    memset(head,0,sizeof(head));
    S=0;T=n*2+1;cnt=1;
    for(int i=1;i<=n;i++)
        add(S,i,1,0),add(i+n,T,1,0);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            add(i,j+n,1,(double)a[i][j]-k*(double)b[i][j]);
    double maxv=0;
    while(spfa()){
        if(maxv<0) return maxv;
        maxv+=up_data();
    }
    return maxv;
}
int main(){
    scanf("%d",&n);int sum=0;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            scanf("%d",&a[i][j]),sum+=a[i][j];
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++){
            scanf("%d",&b[i][j]);
        }
    double l=0,r=(double)sum/(double)n;
    while(r-l>eps){
        double mid=(l+r)/2.0;
        if(work(mid)>0) l=mid;
        else r=mid;
    }
    printf("%.6lf",(l+r)/2.0);
    return 0;
}

 

转载于:https://www.cnblogs.com/harden/p/6706133.html

<think>我们有一个新生舞会问题:需要根据输入的n名新生的信息(姓名、学号、性别),然后处理m个查询,每个查询给出两个人的标识(可能是姓名或学号),判断这两个人是否可以共舞(即性别是否相异)。 思路: 1. 我们需要存储每个新生的信息,并且能够通过姓名或学号快速查找到对应的性别。 2. 由于姓名和学号都是唯一的,我们可以用两个映射(map):一个以姓名为键,一个以学号为键,值存储该人的性别。 3. 对于每个查询,我们会得到两个字符串,代表两个人的标识。我们需要判断每个标识是姓名还是学号?实际上,我们不需要判断,因为姓名和学号在两个不同的映射中。但是注意:题目中姓名和学号是互不相同的,而且任意两人的姓名、学号均互不相同,所以我们可以将两个映射合并为一个映射:将所有的姓名和学号都作为键,映射到对应的性别。因为姓名和学号不会重复(即不会有一个人的姓名和另一个人的学号相同吗?题目没有明确,但注意题目说“每个人可被其姓名或学号唯一确定”,这意味着整个集合中,所有的姓名和学号都是唯一的(即不会出现重名,也不会出现学号重复,而且不会出现某个人的姓名等于另一个人的学号的情况?)。题目没有说姓名和学号之间是否会重复,但为了安全起见,我们可以假设不会重复(因为题目说“任意两人的姓名、学号均互不相同”,这里应该是指任意两个人的姓名不同,任意两个人的学号不同,但并没有说一个人的姓名会不会等于另一个人的学号)。然而,题目要求用姓名或学号唯一确定一个人,所以实际上整个系统中,所有的姓名和学号都是唯一的标识(即不会出现两个相同的字符串,不管是姓名还是学号,只要字符串相同就代表同一个人?)。但是,有可能出现这种情况:一个人A的学号恰好等于另一个人B的姓名?题目没有明确禁止,但根据题目的描述,我们无法排除这种情况。因此,我们需要将姓名和学号视为不同的键,那么就不能合并到一个映射中?但是,我们如何区分一个字符串是姓名还是学号呢? 重新思考:题目要求我们通过姓名或学号来查找,也就是说,输入的一个字符串可能是某个人的姓名,也可能是某个人的学号。而且,题目中说明,任意两人的姓名均不相同,任意两人的学号均不相同。但是,有可能出现一个字符串同时是某个人的姓名和另一个人的学号吗?如果出现这种情况,那么我们就无法确定这个字符串指的是哪个人(因为题目要求唯一确定一个人)。所以,题目中隐含的条件是:所有的姓名和学号构成的集合中,没有重复的字符串(即姓名和学号之间也是互不相同的)。因为如果出现重复,那么就无法唯一确定一个人了。因此,我们可以将所有的姓名和学号都放在一个映射中,键为字符串(姓名或学号),值为性别。 4. 因此,我们可以建立一个map<string, char>,将每个新生的姓名和学号都作为键,映射到该人的性别。注意:同一个人的姓名和学号都要插入到映射中,这样当我们用姓名或学号查询时,都能找到这个人的性别。 但是,同一个人的姓名和学号都映射到同一个性别,这样会不会导致重复?不会,因为每个键都是唯一的(同一个人的姓名和学号不同,而且不同人的姓名和学号都不同)。这样,我们只需要将每个新生的姓名和学号分别作为键插入到map中,值都是同一个性别。 5. 对于每个查询,输入两个字符串s1和s2,我们在map中查找这两个字符串对应的性别。如果其中有一个字符串在map中不存在,那么说明输入有误?但题目没有说这种情况,我们假设输入都是合法的(即每个字符串都是某个人的姓名或学号,且存在)。然后,比较这两个字符串对应的性别是否不同:如果不同,输出"Yes",否则输出"No"。 步骤: 1. 读入整数n。 2. 循环n次,读入每个人的信息:姓名(name)、学号(id)、性别(gender)。 3. 将name作为键,gender作为值插入到map中;同样,将id作为键,gender作为值插入到map中。 4. 读入整数m。 5. 循环m次,每次读入两个字符串s1和s2,然后: 在map中查找s1和s2对应的性别,设为g1和g2。 如果g1 != g2,则输出"Yes";否则输出"No"。 注意:题目中性别用'F'或'M'表示,且只有这两种。 但是,同一个人的姓名和学号都插入到map中,这样不会冲突,因为它们是不同的键。 代码示例: 注意:使用map时,如果键不存在,查找会返回默认值(char的默认值是'\0'),所以我们需要确保查询的键一定存在。根据题目,输入是合法的,所以我们可以直接使用。 然而,为了安全,我们可以在插入时确保每个新生的姓名和学号都是唯一的(不会和之前插入的任何键重复),因为题目已经保证。 实现: 我们可以使用C++的map或者unordered_map(更高效)。由于n最大为1000(题目没有明确,但资源限制内存512MB,时间1.0s,所以n和m不会太大),所以使用map即可。 但是,题目中姓名的长度不超过20,学号不超过10,所以字符串比较不会太慢。 代码结构: #include <iostream> #include <map> #include <string> using namespace std; int main() { int n; cin >> n; map<string, char> personMap; for (int i = 0; i < n; i++) { string name, id; char gender; cin >> name >> id >> gender; // 将姓名和学号都作为键插入 personMap[name] = gender; personMap[id] = gender; } int m; cin >> m; for (int i = 0; i < m; i++) { string s1, s2; cin >> s1 >> s2; char g1 = personMap[s1]; char g2 = personMap[s2]; if (g1 != g2) { cout << "Yes" << endl; } else { cout << "No" << endl; } } return 0; } 但是,这里有一个问题:同一个人的姓名和学号都插入了,那么当我们用学号查询时,得到的是这个人的性别,用姓名查询也是这个人的性别,这符合要求。 但是,如果有一个人的姓名和另一个人的学号相同,那么后插入的会覆盖先插入的吗?根据题目,这种情况不会出现,因为题目说所有的姓名和学号都是唯一的(即整个系统中,所有的字符串都是唯一的),所以不会出现重复的键。 因此,我们按照上述代码即可。 但是,题目中描述“任意两人的姓名、学号均互不相同”,并没有说一个人的姓名和另一个人的学号不会相同。所以,我们需要考虑这种情况:如果一个人的姓名和另一个人的学号相同,那么当我们插入第一个人的姓名和第二个人的学号时,它们作为不同的键插入,没有问题。但是,当我们用这个字符串查询时,我们得到的是哪个人的性别?实际上,这个字符串作为键,在map中只出现一次,它要么是第一个人的姓名,要么是第二个人的学号,不可能同时存在。因为map的键是唯一的,如果两个不同的人有相同的字符串(比如一个人的姓名等于另一个人的学号),那么后插入的就会覆盖先插入的,这样就会导致错误。 因此,我们需要重新审视:题目是否保证姓名和学号之间不会重复?题目说“每个人可被其姓名或学号唯一确定”,这意味着:整个集合中,任意一个字符串(姓名或学号)最多只能对应一个人。所以,不会出现一个字符串同时是两个人的标识(不管是作为姓名还是学号)。因此,我们可以放心地将所有字符串作为键。 所以,上述代码是可行的。 但是,我们还需要注意:同一个人的姓名和学号是不同的字符串,所以插入两个不同的键,分别对应同一个性别,这是正确的。 测试样例: 输入: 1 John 001 M 1 John 001 # 这里查询的是同一个人,性别相同,所以输出No(因为同一个人不能共舞?但是题目要求是两个人,而且这里输入的两个字符串代表同一个人,所以实际上不是两个人,但题目要求判断的是两个人?题目中说“m对两人的信息”,所以每对应该是两个不同的人。因此,输入保证是两个人,即两个不同的字符串(代表两个不同的人)。 但是,如果输入的两个字符串代表同一个人(比如输入了一个人的姓名和学号),那么实际上我们查到的性别是同一个人的两次,所以相同,输出No。但是,题目要求是两个人,所以这种情况应该不会出现?因为题目说“两人”,所以输入的两个字符串一定代表两个不同的人。因此,我们不需要考虑同一个人。 但是,如果输入的两个字符串中,一个字符串是某个人的姓名,另一个字符串是这个人的学号,那么这两个字符串对应同一个人,所以性别相同,输出No。这符合题意:因为只有一个人,无法共舞(需要两个人且性别不同)。但题目要求的是两个人,所以这种情况在题目中不会出现?因为题目说“每对两人的信息”,所以应该是两个不同的人。因此,我们假设输入的两个字符串一定代表两个不同的人。 因此,我们的代码可以这样写。 但是,题目中并没有明确说明输入的查询字符串一定是两个不同的人,所以我们需要考虑:如果出现同一个人的姓名和学号作为两个字符串输入,那么我们的代码会输出No,这是正确的,因为一个人不能和自己跳舞(而且性别相同)。 所以,按照上述思路编写代码。 注意:输入可能有空格,所以使用cin读取字符串时,遇到空格会停止。而题目中姓名和学号都是不含空格的,所以可以用cin。 另外,性别是一个字符,可以用cin读取。 我们编写代码,并处理多个测试用例?题目没有说多组测试,所以只有一组。 但是,题目有资源限制,时间1.0s,内存512MB,所以我们的算法是O((n+m) log n)的,因为map的插入和查找都是O(log n)的,n最大1000(题目没有给出n和m的范围,但根据内存限制,n和m不会太大,因为字符串长度有限,总字符串数最多2000个键),所以可以接受。 代码实现: 注意:题目中新生人数n,查询次数m。 但是,我们考虑一个边界:n=0,m=0。 另外,如果查询的字符串不在map中,我们的代码会返回'\0',然后比较。但题目保证输入合法,所以不会出现这种情况。 为了安全,可以检查一下,如果g1或g2是'\0',则说明输入有误,但题目没有要求,我们可以不检查。 因此,最终代码: 注意:题目要求用C++实现,我们按照上述代码结构。 但是,有一个问题:同一个人的姓名和学号都插入到map中,那么当我们插入n个人时,map中会有2*n个键值对。这是允许的。 开始编码:</think>以下是针对新生舞会问题的C++实现方案,通过姓名或学号判断两人是否可以共舞(性别相异): ```cpp #include <iostream> #include <unordered_map> #include <string> using namespace std; int main() { int n; cin >> n; // 建立姓名/学号到性别的映射 unordered_map<string, char> infoMap; // 读取新生信息 for (int i = 0; i < n; i++) { string name, id; char gender; cin >> name >> id >> gender; infoMap[name] = gender; // 姓名作为键 infoMap[id] = gender; // 学号作为键 } int m; cin >> m; // 处理查询 while (m--) { string s1, s2; cin >> s1 >> s2; // 比较两人性别 if (infoMap[s1] != infoMap[s2]) { cout << "Yes" << endl; } else { cout << "No" << endl; } } return 0; } ``` ### 算法说明 1. **数据结构**:使用`unordered_map`建立字符串(姓名/学号)到性别的映射 - 插入复杂度:$O(1)$ - 查询复杂度:$O(1)$ 2. **核心逻辑**: - 每个新生的**姓名**和**学号**都作为独立键存入映射表 - 查询时直接比较两个标识符对应的性别值 - 性别不同时输出`Yes`,否则输出`No` ### 输入示例 ``` 3 Alice 001 F Bob 002 M Carol 003 F 2 Alice 002 001 003 ``` ### 输出示例 ``` Yes // Alice(F) 和 Bob(M) No // Alice(F) 和 Carol(F) ``` ### 关键特性 1. 利用哈希表实现$O(1)$快速查询 2. 同时支持姓名和学号两种查询方式 3. 正确处理边界情况(如用学号查女生+姓名查男生) 4. 符合题目要求的互异性:姓名和学号全局唯一[^1] [^1]: 试题 算法训练 新生舞会,资源限制时间1.0s,内存512.0MB
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值