[SDOI2017]新生舞会 0/1分数规划

本文介绍了解决一个特定匹配问题的方法,采用0/1分数规划结合费用流算法。通过二分查找最大可行解,并调整边权实现最大费用最大流,确保匹配方案的有效性。

~~~题面~~~

题解:

0/1分数规划,,,但是竟然有诡异的精度问题???因为这个被卡了好久

中途还写过一次KM,,,结果陷入死循环,,,我大概是写了一个假KM,,,于是放弃KM,回来调费用流

这个题面也很直白啊~~~

我们令C>=x,

然后二分求出最大的x即可,

每次跑费用流前重新定义边权

a[i] - mid * b[i]

然后跑最大费用最大流看看跑出来能不能有大于0的费用就可以了

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 #define R register int
  4 #define AC 410
  5 #define ac 40000
  6 #define eps 1e-7
  7 #define inf -9000000000LL
  8 #define getchar() *o++
  9 #define D printf("line in %d\n",__LINE__);
 10 char READ[5000100],*o=READ;
 11 int n,s,t;
 12 double maxn,mid,ans;
 13 int tmp[AC][AC],last[AC];
 14 int Head[AC],Next[ac],date[ac],haveflow[ac],disflow[AC],tot=1;
 15 double dis[ac],cost[ac],a[ac],b[ac];
 16 bool z[AC];
 17 deque <int> q;
 18 /*01分数规划,二分x,然后跑最大费用最大流,看费用是否大于等于0,大于等于0即合法。
 19 1 ~ n 为女生,n+1 ~ 2*n 为男生 ,s = 2*n+1 ,t = 2*n+2*/
 20 
 21 inline int read()
 22 {
 23     int x=0;char c=getchar();
 24     while(c > '9' || c < '0') c=getchar();
 25     while(c >= '0' && c <='9') x=x*10+c-'0',c=getchar();
 26     return x;
 27 }
 28 
 29 inline void add(int f,int w,int aa,int bb)
 30 {
 31     date[++tot]=w,Next[tot]=Head[f],Head[f]=tot,a[tot]=aa,b[tot]=bb;
 32     date[++tot]=f,Next[tot]=Head[w],Head[w]=tot;    
 33 }
 34 
 35 inline void upmax(double &a,double b)
 36 {
 37     if(b > a) a = b;
 38 }
 39 
 40 void pre()
 41 {
 42     int a;
 43     n=read();
 44     s=2 * n + 1,t=2 * n + 2;
 45     for(R i=1;i<=n;i++)
 46         for(R j=1;j<=n;j++)
 47             tmp[i][j]=read();
 48     for(R i=1;i<=n;i++)
 49         for(R j=1;j<=n;j++)
 50         {
 51             a=read();
 52             add(i,n + j,tmp[i][j],a);
 53             upmax(maxn,(double)tmp[i][j] / (double)a);
 54         }
 55     for(R i=1;i<=n;i++) add(s,i,0,0);
 56     for(R i=n+1;i<=2*n;i++) add(i,t,0,0);    
 57 }
 58 
 59 void aru()
 60 {
 61     int x=t;
 62     while(x != s)
 63     {
 64         haveflow[last[x]] -= disflow[x];//是减掉disflow[x]啊
 65         haveflow[last[x] ^ 1] += disflow[x];
 66         x=date[last[x] ^ 1];
 67     }
 68     ans += disflow[t] * dis[t];
 69 
 70 }
 71 
 72 bool spfa()
 73 {
 74     int x,now;
 75     z[s]=true,dis[s]=0,disflow[s]=INT_MAX;
 76     q.push_front(s);
 77     while(!q.empty())
 78     {
 79         x=q.front();
 80         q.pop_front();
 81         z[x]=false;
 82         for(R i=Head[x]; i ;i=Next[i])
 83         {
 84             now=date[i];
 85             if(haveflow[i] && dis[now] < dis[x] + cost[i])
 86             {
 87                 dis[now] = dis[x] + cost[i];
 88                 disflow[now] = min(haveflow[i],disflow[x]);//是用当前边!的haveflow和当前点!的disflow的比较
 89                 last[now] = i;
 90                 if(!z[now] && now != t)//t当然不能进来了 
 91                 {
 92                     if(!q.empty() && dis[now] > dis[q.front()])    q.push_front(now);
 93                     else q.push_back(now);
 94                     z[now]=true;
 95                 }
 96             }
 97         }
 98     }
 99     x=t;
100     if(dis[t] != inf) aru();
101     return dis[t] != inf;
102 }
103 
104 bool check()
105 {
106     int bb=n * 2 + 2;
107     for(R i=1;i<=bb;i++) dis[i]=inf;//要手动inf,,error!!!怎么可以改了下面不改这里,,,
108     ans=0;
109     for(R i=2;i<=tot;i+=2)//枚举正向边
110     {
111         haveflow[i] = 1;//所有边默认流量都为1
112         haveflow[i+1] = 0;//反向边当然是0
113         cost[i] = a[i] - mid * b[i];
114         cost[i+1] = -cost[i];//重新建图 
115     }
116     while(spfa()) //貌似因为是double,所以memset 128不好用了
117         for(R i=1;i<=bb;i++) dis[i] = inf;
118     return ans >= 0;
119 }
120 
121 void half()
122 {
123     double l=0,r=10000;
124     while(r - l > eps)
125     {
126         mid = (l + r) / 2;//因为是实数,所以没有什么偏向问题
127         if(check()) l = mid;
128         else r = mid;//error!!!!!!!!所以说是因为-eps才错的?
129     }//啊啊啊啊啊啊啊啊啊啊那为什么不能减啊!!!!!!!!
130     printf("%.6lf\n",l);
131 }
132 
133 int main()
134 {
135 //    freopen("in.in","r",stdin);
136     fread(READ,1,5000000,stdin);
137     pre();
138     half();
139 //    fclose(stdin);
140     return 0;
141 }

 

转载于:https://www.cnblogs.com/ww3113306/p/9145659.html

### PTA 周末舞会问题分析 该问题是关于模拟男士和女士排队并配对的过程。题目描述了一个场景,在这个场景中,男士和女士分别排成两个队列,并按照一定的规则进行配对[^1]。 #### 解决方案概述 为了完成此任务,可以采用简单的循环结构来处理男女队伍的匹配过程。以下是解决问题的主要思路: - 初始化两个列表 `men` 和 `women` 来表示男性和女性的队列。 - 使用一个计数器记录当前正在播放的音乐编号。 - 对于每首音乐,尝试从两个队列头部取出一个人组成一对。 - 如果某个队列为空,则跳过该次配对操作。 - 记录每次成功配对的结果以及剩余未配对的人。 下面是基于 Python 的具体实现代码: ```python def dance_pairing(m, n, k): men = list(range(1, m + 1)) # 创建男性队列 [1, 2, ..., m] women = list(range(1, n + 1)) # 创建女性队列 [1, 2, ..., n] result = [] # 存储最终结果 for music_number in range(k): # 循环k首歌曲 pair = [] if men: # 若男性队列还有人 man = men.pop(0) # 取出第一位男性 pair.append(man) if women: # 若女性队列还有人 woman = women.pop(0) # 取出第一位女性 pair.append(woman) while len(pair) < 2: # 补齐长度至2(如果某一方已经耗尽) pair.append(0) result.append((music_number + 1,) + tuple(pair)) return result # 输入样例 m, n, k = map(int, input().split()) # 获取输入数据 (m=男性数量, n=女性数量, k=音乐数量) output = dance_pairing(m, n, k) # 输出结果 for line in output: print(*line) ``` #### 结果解释 上述函数接受三个参数:`m`, `n`, 和 `k` 分别代表男性人数、女性人数和音乐总数。它返回的是一个元组列表,其中每个元组包含三部分信息: - 当前音乐序号; - 成功配对的男性 ID(如果没有则为 0); - 成功配对的女性 ID(如果没有则为 0)。 例如,对于输入 `(3, 4, 5)`,可能得到如下输出: ``` 1 1 1 2 2 2 3 3 3 4 0 4 5 0 0 ``` 这表明第 1 至第 3 轮有完整的配对;第 4 轮仅有女性参与;而第 5 轮无人可配对。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值