BZOJ 4429 [Nwerc2015] Elementary Math小学数学 二分图匹配

本文介绍了一个自动化构建数学考试的方法,确保每道题目的答案各不相同,通过使用图论中的二分图匹配算法来实现这一目标。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

Ellen is teaching elementary math to her students and the time for the final exam has come. The exam consists of n questions. In each question the students have to add (+), subtract (−) or multiply (∗) a pair of numbers.
Ellen has already chosen the n pairs of numbers. All that remains is to decide for each pair which of the three possible operations the students should perform. To avoid students getting bored, Ellen wants to make sure that the n correct answers to her exam are all different.
Please help Ellen finish constructing the exam by automating this task.
Ellen给她的学生教小学数学。期末考试已经来临了。考试有n个题目,每一个题目学生们都要对一对数字进行加(+),减(-),乘(*)运算。
Ellen已经选好了n对数。剩下的是决定学生们应该对每对数执行什么运算。为了不让学生们感到厌烦,Ellen想确保n个正确答案都不一样。
请帮助Ellen自动化地构建考试。

Input

The input consists of:
one line with one integer n (1≤n≤2500), the number of pairs of numbers;
n lines each with two integers a and b (−10^6≤a,b≤10^6), a pair of numbers used.
输入包括:
第一行是一个整数n(1<=n<=2500),表示共有n道题目。
接下来n行每行有2个整数a和b(-10^6<=a,b<=10^6),表示每一题使用的整数。

Output

For each pair of numbers (a,b) in the same order as in the input, output a line containing a valid equation. Each equation should consist of five parts: a, one of the three operators, b, an equals sign (=), and the result of the expression. All the n expression results must be different.
If there are multiple valid answers you may output any of them. If there is no valid answer, output a single line with the string “impossible” instead.
对于输入中的每一对(a,b),输出一行有效的方程。每一个方程应该包含5部分:a,+、-、*中的一个运算符,b,=,答案。N个答案必须不同。
如果有多个有效答案,你可以输出任意一个。如果没有答案,输出“impossible”。

Sample Input

Sample input 1
4
1 5
3 3
4 5
-1 -6
Sample input 2
4
-4 2
-4 2
-4 2
-4 2

Sample Output

Sample output 1
1 + 5 = 6
3 * 3 = 9
4 - 5 = -1
-1 - -6 = 5

Sample output 2
impossible

HINT












考虑到每个算式只有+,-,*最多3种结果,
我们把第i个算式向它的三种结果格连一条边。
那么很显然,跑一个二分图匹配,如果是完备匹配(匹配数为n)那么肯定有解,
不然就无解。
有解怎么输出这个解呢?
可以看到二分图匹配里我们一直在用一个match数组,
但是一般的题目都用不到它,
这题就由它出马了233= =
只要根据match数组对应输出即可。

还有一个问题就是计算结果是很大的,
那么就取出来离散化一下就好了。
最后如何输出已经在程序里面标注了。




#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int
    N=2505;
int n,Ecnt,tag,trank;
int x[N],y[N],vis[N];
int b[N+N+N],match[N<<2];
ll ANS[N],c[N+N+N];
struct Edge{
    int next,to;
}E[N+N+N];int head[N];
struct Exp{
    ll num;int id;
}a[N+N+N];
bool cmp(Exp a,Exp b){
    return a.num<b.num;
}
void add(int u,int v){
    E[++Ecnt].next=head[u];
    E[Ecnt].to=v;
    head[u]=Ecnt;
}
bool dfs(int u){
    int t;
    for (int i=head[u];i;i=E[i].next){
        int v=E[i].to;
        if (vis[v]==tag) continue;
        vis[v]=tag;
        t=match[v],match[v]=u;
        if (t==-1 || dfs(t)) return 1;
        match[v]=t;
    }
    return 0;
}
void disc(int m){
    sort(a+1,a+1+m,cmp);
    trank=0;
    a[0].num=(ll)-1e6*1e6-1;
    for (int i=1;i<=m;i++)
        if (a[i].num==a[i-1].num) b[a[i].id]=trank;
            else b[a[i].id]=++trank;
    trank=0;
    for (int i=1;i<=m;i++)
        if (a[i].num!=a[i-1].num) c[++trank]=a[i].num;
}
void build(){
    Ecnt=0;int tmp=0;
    for (int i=1;i<=n;i++)
        for (int j=1;j<=3;j++) add(i,b[++tmp]+n);
}
int main(){
    scanf("%d",&n);
    int m=0;
    for (int i=1;i<=n;i++){
        scanf("%d%d",&x[i],&y[i]);
        a[++m].num=x[i]+y[i],a[m].id=m;
        a[++m].num=x[i]-y[i],a[m].id=m;
        a[++m].num=(ll)x[i]*y[i],a[m].id=m;
    }
    disc(m),build();
    tag=0;int ans=0;
    memset(match,255,sizeof(match));
    for (int i=1;i<=n;i++){
        tag++;
        if (dfs(i)) ans++;
    }
    if (ans!=n) puts("impossible");
        else{
            //如何输出********
            for (int i=n+1;i<=n+trank;i++)
                if (match[i]>0) ANS[match[i]]=c[i-n];
            for (int i=1;i<=n;i++){
                printf("%d ",x[i]);
                if (x[i]+y[i]==ANS[i]) printf("+ %d = %d",y[i],x[i]+y[i]); else
                if (x[i]-y[i]==ANS[i]) printf("- %d = %d",y[i],x[i]-y[i]); else
                if ((ll)x[i]*y[i]==ANS[i]) printf("* %d = %lld",y[i],(ll)x[i]*y[i]);
                putchar('\n');
            }
        }
    return 0;
}

### BZOJ1461 字符串匹配 题解 针对BZOJ1461字符串匹配问题,解决方法涉及到了KMP算法以及树状数组的应用。对于此类问题,朴素的算法无法满足时间效率的要求,因为其复杂度可能高达O(ML²),其中M代表模式串的数量,L为平均长度[^2]。 为了提高效率,在这个问题中采用了更先进的技术组合——即利用KMP算法来预处理模式串,并通过构建失配树(也称为失败指针),使得可以在主串上高效地滑动窗口并检测多个模式串的存在情况。具体来说: - **前缀函数与KMP准备阶段**:先对每一个给定的模式串执行一次KMP算法中的pre_kmp操作,得到各个模式串对应的next数组。 - **建立失配树结构**:基于所有模式串共同构成的一棵Trie树基础上进一步扩展成带有失配链接指向的AC自动机形式;当遇到某个节点不存在对应字符转移路径时,则沿用该处失配链路直至找到合适的目标或者回到根部重新开始尝试其他分支。 - **查询过程**:遍历整个待查文本序列的同时维护当前状态处于哪一层级下的哪个子结点之中,每当成功匹配到完整的单词就更新计数值至相应位置上的f_i变量里去记录下这一事实。 下面是简化版Python代码片段用于说明上述逻辑框架: ```python from collections import defaultdict def build_ac_automaton(patterns): trie = {} fail = [None]*len(patterns) # 构建 Trie 树 for i,pattern in enumerate(patterns): node = trie for char in pattern: if char not in node: node[char]={} node=node[char] node['#']=i queue=[trie] while queue: current=queue.pop() for key,value in list(current.items()): if isinstance(value,int):continue if key=='#': continue parent=current[key] p=fail[current is trie and 0 or id(current)] while True: next_p=p and p.get(key,None) if next_p:break elif p==0: value['fail']=trie break else:p=fail[id(p)] if 'fail'not in value:value['fail']=next_p queue.append(parent) return trie,fail def solve(text, patterns): n=len(text) m=len(patterns) f=[defaultdict(int)for _in range(n)] ac_trie,_=build_ac_automaton(patterns) state=ac_trie for idx,char in enumerate(text+'$',start=-1): while True: trans=state.get(char,state.get('#',{}).get('fail')) if trans!=None: state=trans break elif '#'in state: state[state['#']['fail']] else: state=ac_trie cur_state=state while cur_state!={}and'#'in cur_state: matched_pattern_idx=cur_state['#'] f[idx][matched_pattern_idx]+=1 cur_state=cur_state['fail'] result=[] for i in range(len(f)-1): row=list(f[i].values()) if any(row): result.extend([sum((row[:j+1]))for j,x in enumerate(row[::-1])if x>0]) return sum(result) patterns=["ab","bc"] text="abc" print(solve(text,text)) #[^4] ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值