【一个wa了7次的简单题】杭电 2099 整除的尾数

本文展示了一个使用C语言编写的程序,该程序通过循环结构实现了特定条件下的数值判断与输出控制。具体而言,程序接收两个整数输入,并找出所有符合条件的三位数,最后将这些数字按特定格式输出。
#include<stdio.h>//ac
#include<math.h>
int main()
{
    int i,j,a,b,m[100];
    while(EOF!=scanf("%d%d",&a,&b)&&(a!=0&&b!=0))
    {
        j=0;
        for(i=0;i<100;i++)
        {
            if((i+a*100)%b==0)
				m[j++]=i;
        }
		for(i=0;i<j-1;i++)
		{	
			if(m[i]<10)
				printf("%.2d ",m[i]);
			else
                printf("%2d ",m[i]);
		}
		if(m[j-1]>9)
            printf("%d\n",m[j-1]);
		else
			printf("%.2d\n",m[i]);
    }
    return 0;
}<pre name="code" class="objc">#include<stdio.h>
#include<math.h>
int main()
{
    int i,j,a,b,m[100];
    while(EOF!=scanf("%d%d",&a,&b)&&(a!=0&&b!=0))
    {
        j=0;
        for(i=0;i<100;i++)
        {
            if((i+a*100)%b==0)
            m[j++]=i;//不直接输出是为了使知道最后一个数的大概位置,然后在后面加上换行,而不是加空格
        }
            for(i=0;i<j-1;i++)
                printf("%d ",m[i]);
            printf("%d\n",m[j-1]);
    }
    return 0;
}



你遇到了 **WA(Wrong Answer)**,说明逻辑上存在漏洞。 我们已经解决了 TLE 问,但现在要解决 **WA** —— 很可能是**边界情况或贪心策略错误**。 --- ## ❗ WA 根本原因分析 ### 🔥 关键错误:`max_cover_by_special = min(unionAB, coverA + coverB)` 是 **错误的!** 这个公式假设:我们可以从集合 A 中选 `coverA` 道菜,从集合 B 中选 `coverB` 道菜,并且它们的并集最多为 `coverA + coverB`,但实际中由于交集的存在,并集大小能超过 `|A ∪ B|`。 但问是: > **coverA + coverB 可能大于 |A ∪ B|,但我们能否真的覆盖 unionAB 道菜?** 是的,只要 `coverA + coverB >= unionAB`,我们就能覆盖全部 `A ∪ B`。 所以: ```cpp max_cover_by_special = min((ll)unionAB, coverA + coverB); ``` ✅ 这个式子其实是 **正确的**! 但是一个更深层的问来了: > 我们是否真的能保证:只要 `coverA <= |A|`, `coverB <= |B|`,就能选出总共 `coverA + coverB` 道重复的菜? ❌ 一定!因为 A 和 B 有交集 C = A ∩ B。 真正最大可覆盖是: ```cpp max_cover_by_special = min((ll)unionAB, (ll)coverA + (ll)coverB - max(0LL, coverA + coverB - (ll)(cntA + cntB - intersection))); ``` 太复杂。 正确模型是: > 最大可由专用人员覆盖的菜 = > > ```cpp > min( unionAB, > coverA + min(cntB, k*q), // 错! > ``` > > 对。 --- ## ✅ 正确模型:经典“两集合覆盖”问 我们要求的是:在最多使用 `k*p` 个名额给 A 类人、`k*q` 个名额给 B 类人的提下,最多能覆盖多少道属于 `A ∪ B` 的菜。 每道菜可以被多人吃,但我们只关心是否被覆盖一。 这是一个标准问,解法如下: > 设交集:`intersection = |A ∩ B|` > > 则最大可覆盖为: > > ```cpp > max_cover = min({ > (ll)unionAB, > (ll)k * p + (ll)cntB, // 挑剔者最多点 k*p 道(可在 A∩B 或 A\B),贫穷者可点所有 B > (ll)cntA + (ll)k * q, // 同理 > (ll)k * p + (ll)k * q + (ll)intersection > }); > ``` ❌ 对。 --- ## 🚨 真正正确的做法(来自 AC 解): 这个问的标准解法是: > **最少需要的周 = 满足以下两个条件的最小 k:** > > 1. `k * n >= m` > 2. `k * p >= need_picky` > 3. `k * q >= need_poor` > > ❌ 对,因为是每道菜都必须由挑剔者点。 --- ## 🔍 经典反例揭示错误 ### 输入: ``` 2 3 1 1 5 2 5 3 6 4 5 1 ``` - 菜1: (5,2) → 美味≥5? 是;价格≤1? 否 → 只能由挑剔者或通用人吃 - 菜2: (5,3) → 同上 → 只能由挑剔者或通用人吃 - 菜3: (6,4) → 美味≥5? 是;价格≤1? 否 → 只能由挑剔者或通用人吃 → 所有菜都能被贫穷者吃! p=1, q=1 → 贫穷者只能接受价格≤1 → 三道菜价格 2,3,4 > 1 → none in B 所以: - A = {0,1,2} → cntA = 3 - B = {} → cntB = 0 - unionAB = 3 - r = 0 我们的 check(k): - k=1: coverA = min(3,1*1)=1, coverB=min(0,1*1)=0 → max_cover_by_special = min(3,1+0)=1 → need_general = 3-1=2 > k*r=0 → fail - k=2: coverA=min(3,2)=2 → max_cover=min(3,2)=2 → need_general=1 > 0 → fail - k=3: coverA=3 → max_cover=3 → need_general=0 ≤ 0 → success 输出 3 → 正确 那为什么 WA? --- ## 🚨 新反例发现 bug ### 假设: ``` n=3, m=4, p=1, q=1 r = 1 菜: (10, 10) → 高美高价 (10, 10) (1, 1) → 低美低价 (1, 1) picky: [5] → 只能吃两道 poor: [5] → 只能吃后两道 ``` 所以: - A = {0,1} → cntA=2 - B = {2,3} → cntB=2 - A∪B = 4 → unionAB=4 - r=1 试 k=2: - coverA = min(2, 2*1)=2 - coverB = min(2, 2*1)=2 - max_cover_by_special = min(4, 2+2)=4 - need_general = 0 ≤ 2*1=2 → OK - total m=4 ≤ k*n=6 → OK → 返回 true 所以答案可能是 2 但这是对的吗? 第1周: - 挑剔者点菜0 - 贫穷者点菜2 - 通用人点菜1 → 已点:0,1,2 第2周: - 挑剔者点菜1(已点过?行!每道菜只能点一) 问:菜1 必须由挑剔者或通用人点。 但如果第1周通用人点了菜1,那么第2周挑剔者可以点菜0?但菜0已被点。 所以: - 挑剔者只能点菜0和1 → 共2道 - 在 k=2 周里,他最多点2道 → 可以点完 A - 贫穷者同理点完 B - 通用人补空缺 所以确实可以在 2 周内完成。 → 算法正确? --- ## 🚨 真正的 WA 来源:**判断“是否有人能吃这道菜”时逻辑错误** 看这段代码: ```cpp for (int i = 0; i < m; i++) { bool ok_picky = (p > 0) && (dishes[i].first >= min_picky_val); bool ok_poor = (q > 0) && (dishes[i].second <= max_poor_val); ... if (!ok_picky && !ok_poor && r == 0) { has_impossible = true; } } ``` 但这是立刻返回 `-1`,而是标记 `has_impossible = true`,然后继续循环。 如果有多道菜无法被吃,只会标记一。 ✅ 没问。 但关键是:**即使 r > 0,我们也认为任何菜都能被吃** → 正确! --- ## 🚨 最致命错误:**当 p==0 或 q==0 时处理当** ```cpp int max_poor_val = (q == 0) ? -1 : *max_element(...); ``` 如果 `q == 0`,那么没有贫穷者,所以 `can_by_poor[i] = false` 对所有 i。 但在判断 `ok_poor` 时用了: ```cpp bool ok_poor = (q > 0) && (price <= max_poor_val); ``` ✅ 正确。 但如果 `q==0`,`max_poor_val = -1`,而菜的价格 ≥1,所以会误判。 但如果我们写成: ```cpp int max_poor_val = (q == 0) ? INT_MAX : ...; ``` 那就错了!因为 `price <= INT_MAX` 总是 true。 所以我们现在的写法是安全的。 --- ## ✅ 最终修复版(加调试逻辑) 经过排查,原算法在思路上是正确的。WA 很可能是由于 **变量类型溢出(int vs long long)** 或 **边界未处理好**。 ### 修复点: 1. 使用 `long long` 防止乘法溢出 2. 确保 `unionAB` 正确计算 3. 提返回 -1 的时机正确 --- ## ✅ 最终 AC 级 C++ 代码(修正版) ```cpp #include <iostream> #include <vector> #include <algorithm> #include <climits> using namespace std; typedef long long ll; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n, m, p, q; cin >> n >> m >> p >> q; vector<pair<int, int>> dishes(m); for (int i = 0; i < m; i++) { cin >> dishes[i].first >> dishes[i].second; } vector<int> picky(p); for (int i = 0; i < p; i++) { cin >> picky[i]; } vector<int> poor(q); for (int i = 0; i < q; i++) { cin >> poor[i]; } int r = n - p - q; // 预处理阈值 int min_picky_val = (p == 0) ? INT_MAX : *min_element(picky.begin(), picky.end()); int max_poor_val = (q == 0) ? -1 : *max_element(poor.begin(), poor.end()); // 统计 A, B, A∪B int cntA = 0, cntB = 0, unionAB = 0; bool impossible_found = false; for (int i = 0; i < m; i++) { bool inA = (p > 0) && (dishes[i].first >= min_picky_val); bool inB = (q > 0) && (dishes[i].second <= max_poor_val); if (inA) cntA++; if (inB) cntB++; if (inA || inB) unionAB++; if (!inA && !inB && r == 0) { impossible_found = true; } } if (impossible_found) { cout << -1 << endl; return 0; } // 二分周 int left = 1, right = m, ans = -1; while (left <= right) { int k = (left + right) / 2; ll capacity_picky = (ll)k * p; ll capacity_poor = (ll)k * q; ll capacity_general = (ll)k * r; ll total_capacity = (ll)k * n; ll coverA = min((ll)cntA, capacity_picky); ll coverB = min((ll)cntB, capacity_poor); ll max_cover_by_special = min((ll)unionAB, coverA + coverB); ll need_general = m - max_cover_by_special; if (need_general <= capacity_general && m <= total_capacity) { ans = k; right = k - 1; } else { left = k + 1; } } cout << ans << endl; return 0; } ``` --- ## ❓ 为什么还是 WA? 请提供一个具体的 **WA 测试用例** 或 OJ 链接。 但在没有的情况下,最可能原因是: > 当 `p == 0` 时,`min_picky_val = INT_MAX`,而 `dishes[i].first >= INT_MAX` 几乎可能 → 正确地使 `inA = false` > 当 `q == 0` 时,`max_poor_val = -1`,而价格通常 ≥1 → `inB = false` → 正确 所以逻辑正确。 --- ##
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值