2017 SCNUCPC 解题报告

校内赛题目、解题思路、参考代码一览

A. Blackstorm’s Blackstore

Problem Description

Blackstorm is going to open a blackstore. He now need to transport some goods.

There are nn towns, numbered from 11 to nn. Goods are now at Town 11 while the destination is at Town nn. mm directed roads are connecting the towns, the iith of which has values Ai,Bi,Pi,CiA_i, B_i, P_i, C_i, meaning that this road connects from AiA_ith town to BiB_ith one, which can transport at most CiC_i kilogram of goods at PiP_i yuan per kilo. He wants to know how many goods can reach the destination with KK yuan.

Blackstorm is going to sleep. Can you help him solve this problem?

Input

Multiple test cases, process till end of file. For each case:

On the first line there are three numbers, n,m,Kn, m, K, described as the description says.

Following are mm lines, on the iith of which are four numbers Ai,Bi,Pi,CiA_i, B_i, P_i, C_i.

All the numbers except KK are integers between [1,100][1, 100]. KK is integer between [0,100000][0, 100000]. It's also guaranteed that Ai,BinA_i, B_i \leq n.

Output

For each test case, output "Case #TT: ansans" (without quotes), where TT is the number of the cases, and ansans is the maximum transporting mass with 4 digits after the decimal point.

Sample Input

3 3 5
1 3 1 1
1 2 1 5
2 3 1 5

Sample Output

Case \#1: 3.0000

Hint

One yuan on road 1->3, two on 1->2 and two on 2->3.

解题思路

  • 网络流版题

参考代码

#include <cstring>
#include <queue>
using namespace std;
const int MAXN = 10000;
const int MAXM = 100000;
const int INF = 0x3f3f3f3f;
struct Edge {
    int to,next,cap,flow,cost;
} edge[MAXM];
int head[MAXN],tol;
int pre[MAXN],dis[MAXN];
bool vis[MAXN];
int N;//节点总个数,节点编号从0~N-1
void init(int n) {
    N = n;
    tol = 0;
    memset(head,-1,sizeof(head));
}
void addedge(int u,int v,int cap,int cost) {
    edge[tol].to = v;
    edge[tol].cap = cap;
    edge[tol].cost = cost;
    edge[tol].flow = 0;
    edge[tol].next = head[u];
    head[u] = tol++;
    edge[tol].to = u;
    edge[tol].cap = 0;
    edge[tol].cost = -cost;
    edge[tol].flow = 0;
    edge[tol].next = head[v];
    head[v] = tol++;
}
bool spfa(int s,int t) {
    queue<int>q;
    for(int i = 0; i < N; i++) {
        dis[i] = INF;
        vis[i] = false;
        pre[i] = -1;
    }
    dis[s] = 0;
    vis[s] = true;
    q.push(s);
    while(!q.empty()) {
        int u = q.front();
        q.pop();
        vis[u] = false;
        for(int i = head[u]; i != -1; i = edge[i].next) {
            int v = edge[i].to;
            if(edge[i].cap > edge[i].flow &&
                    dis[v] > dis[u] + edge[i].cost ) {
                dis[v] = dis[u] + edge[i].cost;
                pre[v] = i;
                if(!vis[v]) {
                    vis[v] = true;
                    q.push(v);
                }
            }
        }
    }
    if(pre[t] == -1)return false;
    else return true;
}
int k;
//返回的是最大流,cost存的是最小费用
double minCostMaxflow(int s,int t) {
    int flow = 0;
    int cost = 0;
    while(spfa(s,t)) {
        int Min = INF;
        for(int i = pre[t]; i != -1; i = pre[edge[i^1].to]) {
            if(Min > edge[i].cap - edge[i].flow)
                Min = edge[i].cap - edge[i].flow;
        }
        int costper = 0;
        for(int i = pre[t]; i != -1; i = pre[edge[i^1].to]) {
            edge[i].flow += Min;
            edge[i^1].flow -= Min;
            costper += edge[i].cost;
        }
        int tmpcost = cost + costper * Min;
        if (tmpcost>=k) {
            return flow + (k-cost)/(double)costper;
        }
        cost = tmpcost;
        flow += Min;
    }
    return flow;
}
#include <cstdio>
int main() {
    int n,m,i,T=0;
    while (~scanf("%d%d%d", &n,&m,&k)) {
        init (n+2);
        for (i=0; i<m; i++) {
            int a,b,p,c;
            scanf("%d%d%d%d",&a,&b,&p,&c);
            addedge(a,b,c,p);
        }
        printf ("Case #%d: %.4f\n", ++T, minCostMaxflow(1,n));
    }

}

### B. 龟兔慢跑

Problem Description

乌龟和兔子在绕圈跑,它们同时同地出发,问出发后第一次相遇的用时。圈长nn米,乌龟速度ww米/分钟,兔子速度tt米/分钟,且乌龟和兔子每跑mm米都会休息11分钟再继续跑。

Input

首行一个整数TT代表TT组测试数据,接下来TT行每行4个整数分别是 n,m,t,wn, m, t, w。其中 0<n<1090<n<10^9, 0<w<t<m<1000<w<t<m<100

Output

每组测试数据输出一行一个整数,表示相遇时间。测试数据保证使得相遇时间为整数。

Sample Input

1
100 100 25 20

Sample Output

5

Hint

64位整型long long int请使用%lld输出

解题思路

  • 模拟

参考代码

#include <cstdio>
#include <cmath>
using namespace std;

int n,m,t,w;

void solve()
{
    int cycle=(m+t)*(m+w);
    int ds=m*(t-w);
    int cycle_num=0,dn=0;
    if(n%ds==0)
        dn=ds,cycle_num=n/ds-1;
    else
        dn=n%ds,cycle_num=n/ds;
    long long int i=0,ans=1;
    char stopw=0,stopt=0;
    double dw=0,dt=0,mw=0,mt=0;
    while(true)
    {
        i++;
        if(stopw)
            dw+=mw,stopw=0;
        else if(mw+w>=m)
            dw+=m-mw,mw=mw+w-m,stopw=1;
        else
            dw+=w,mw+=w;
        if(stopt)
            dt+=abs(mt),stopt=0;
        else if(mt+t>=m)
            dt+=m-mt,mt=mt+t-m,stopt=1;
        else
            dt+=t,mt+=t;
        /*
        printf("cycle=%d,cyclenum=%d,dn=%d,i=%I64d,dt=%f,dw=%f\n",cycle,cycle_num,dn,i,dt,dw);
        if(i%10==0){
            char s;
            scanf("%c",&s);
        }
        */

        if(abs(dn+dw-dt)<0.00001 )
        {
            ans=i+(long long int)cycle_num*cycle;
            break;
        }
        if(abs(dw-dt)<0.00001)
        {
            ans=i;
            break;
        }
    }
    printf("%I64d\n",ans);
}

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d%d%d",&n,&m,&t,&w);
        solve();
    }
    return 0;
}

### C. Snake

解题思路

  • 瞎模拟,原题。

参考代码


### D. Zyj pessimistic in competition

Problem Description

There's only 10 minutes left for the ACM competition(yes the contest you're having right now). Others have already packed their pack ready to leave, while Zyj is just opening his eyes. Zyj can see how many problems any one have done, and he decided to have his rank between (a,b)(a,b). So how many ways are there for him? You can assume that others have penality less than 290min, so the rank of Zyj is totally decided by his answered problem, not his answering time.

Input

The first line contains an integer TT, representing the number of tests. For each test,

There are 4 numbers m,n,a,bm,n,a,b seperated by spaces, meaning that there are mm problems in this test(0<m<270<m<27 due to the number of letters) and nn participants other than Zyj himself. Zyj's rank should fall in (a,b)(a,b).

The following line shows how many seconds Zyj needs to solve each problem(for each time tt, 0t<9990 \leq t < 999, and t have at most 6 digits after the decimal point).

Next are nn lines, each contain mm numbers, the jjth number on the iith line showing whether the iith man have solved the jjth problem, where 1 means he did and 0 means he didn't.

Output

For each test case, output Case #k: AnsAns, which means that in the kkth case there are AnsAns ways.

Sample Input

1
4 1 0 2
300 300 300 300
0 0 0 1

Sample Output

Case \#1: 6

Hint

For a rank in (0,2)(0,2), or say the first, Zyj needs to solve 2 problems at least. Since he needs 5 minutes (300 s) to solve one, he can only solve 2 in 10 minutes. Thus there are C42=6C_4^2=6 ways.

解题思路

  • 原题改改又是一年,二分。

参考代码

出题人的代码太丑了,我不敢放上来。


### E. How many LLM

Problem Description

LLM is also known as L2M. Find out how many of them are there.

Input

Multiple test cases, one per line.

On each line is a string. You need to find out how many LLM(L2M) are there. LLM(L2M)s needn't be continious, but any two can't overlap.

Output

One number per test cases, indicating how many LLM(L2M) are there at most.

Sample Input

LLL2MM
2LLLMM

Sample Output

2
1

解题思路

  • 从后往前遍历,为每个M找L。如果先找到2可以当L用,凑够两个就消掉了。

参考代码

#include <stdio.h>
#include <string.h>

char str[10000007];

int main() {
    while (gets(str)) {
        int L=strlen(str), i, m=0, n=0, t=0;
        for (i=L; i--; ) {
            if (str[i] == 'M')
                m++;
            if (str[i] == '2' && m)
                m--, t++;
            if (str[i] == 'L') {
                if (t) {
                    t--;
                    n++;
                } else if (m)
                    t++, m--;
            }
        }
        printf ("%d\n", n);
    }
}

### F. Fabonacci

Problem Description

我们知道Fabonacci数组通项函数定义如下:

F(1)=1,F(2)=2F(1)=1,F(2)=2
F(N)=F(N1)+F(N2),N>2F(N)=F(N-1)+F(N-2), N>2

现在给你MM个各不相同的正整数A1,A2,,AMA_1,A_2,\cdots,A_M,问你能不能找到一个正整数SS使得F(S)=F(A1)+F(A2)++F(AM)F(S)=F(A_1)+F(A_2)+\cdots+F(A_M)

Input

第一行输入一个整数TT,表示有TT组测试数据。对于每一组测试数据,第一行是一个正整数MM,第二行是MM个正整数A1,A2,,AMA_1,A_2,\cdots,A_M

0<M<100000<M<10000

0<Ai<10000000<A_i<1000000

Output

输出有TT行,每一行对应一组测试数据。
对于每组测试数据,如果能找到满足的SS,则输出SS,否则输出1-1

Sample Input

2  
2  
4 5  
3  
4 5 6

Sample Output

6  
-1

解题思路

  • 首先注意:MM个不各不相同的正整数。如果存在相同,或者在模域上求,则是难度更大的解法。
  • 性质:F(AM)<F(S)<F(AM+2)F(A_M) < F(S) < F(A_M+2),即当有解时只可能为S=AM+1S = A_M + 1
  • 证明:
    1. 显而易见得,F(S)>F(AM)F(S) > F(A_M),且F(S)=i=1MF(Ai)<i=1AMF(i)F(S) = \sum_{i=1}^{M} F(A_i) < \sum_{i=1}^{A_M} F(i)
    2. i=1NF(i)=1+F(1)+F(2)++F(N)1=F(2)+F(1)+F(2)++F(N)1\sum_{i=1}^{N} F(i) = 1+F(1)+F(2)+\cdots+F(N)-1 = F(2)+F(1)+F(2)+\cdots+F(N)-1
    3. i=1NF(i)=F(3)+F(2)+F(3)++F(N)1==F(N)+F(N1)+F(N)1=F(N+2)1\sum_{i=1}^{N} F(i) = F(3)+F(2)+F(3)+\cdots+F(N)-1 = \cdots = F(N)+F(N-1)+F(N)-1 = F(N+2) - 1
    4. 所以F(AM)<F(S)<F(AM+2)1<F(AM+2)F(A_M) < F(S) < F(A_M+2)-1 < F(A_M+2),根据夹逼法则,SS只可能存在唯一解S=AM+1S = A_M + 1
  • 推理:F(S)=F(AM)+F(AM1)=F(AM)+F(AM2)+F(AM3)=F(S)=F(A_M)+F(A_M-1)=F(A_M)+F(A_M-2)+F(A_M-3)=\cdots,可知若SS有解,则数列AA必须满足前两项相差11,后面的项与前一项相差22
  • 贪心:由于数列AA各不相同,设有序,则只有当A1+1=A2A_1+1=A_2时,F(A1)F(A_1)F(A2)F(A_2)可加(否则不能解出S=F1(F(S))S=F^{-1}(F(S)));设相加结果为Ai(i2)A_i'(i \geq 2),同理只有当Ai+1=Ai+1A_i'+1=A_{i+1}时其在FF上的像可加。因此每次取最小两个AxA_x相加能得到全局解SS,若存在一次不可加(即最小两个AxA_x相差大于11),则调整前面任何一个都不能与之相加,选取后面不相邻的任何一个都不能与之相加;特殊情况时数列AA及其子数列可划分为两部分各自可加,由于前部分最大值和后部分最小值相差大于11,因此两部分整体不可加,由此得证贪心法正确性。
  • 时间复杂度:O(MlogM+M)O(MlogM + M)

出题人提供的题解

  • 不失一般性的,我们可以假设A1<A2<<AMSA_1<A_2<\cdots<A_M\leq S
  • 由于F(S)=F(S2)+F(S3)++F(2)+F(1)+2F(S)=F(S-2)+F(S-3)+\cdots+F(2)+F(1)+2
  • 假设S>AM+1S>A_M+1,则明显有F(S)F(A1)+F(A2)++F(AM)F(S)\neq F(A_1)+F(A_2)+\cdots+F(A_M),假设不成立。
  • 假设S=AMS=A_M,则只有M=1M=1时才成立。
  • 假设S=AM+1S=A_M+1,使F(S)=F(A1)+F(A2)++F(AM)F(S)=F(A_1)+F(A_2)+\cdots+F(A_M)满足的充分必要条件是F(AM1)=F(A1)+F(A2)++F(AM1)F(A_M-1)=F(A_1)+F(A_2)+\cdots+F(A_{M-1})。如此不断归约,问题最终会化为F(A31)=F(A1)+F(A2)F(A_3-1)=F(A_1)+F(A_2)
  • 综合上面的式子,得到满足条件F(A1)=F(A2)=F(A31),F(A31)+F(A3)=F(A41)F(A_1)=F(A_2)=F(A_3-1),F(A_3-1)+F(A_3)=F(A_4-1)。即应该满足A1=A21,A2=A32,,AM1=AM2A_1=A_2-1,A_2=A_3-2,\cdots,A_{M-1}=A_M-2

参考代码

#include <cstdio>

int main() {
    scanf("%d", &t);
    while(t--) {
        scanf("%d", &n);
        for(int i = 0; i < n; ++i) scanf("%d", a + i);
        if(n < 2) printf("%d", a[0]);
        else {
            sort(a, a  + n);
            bool flag = true;
            int ans;
            if(a[0] == a[1] - 1) {
                ans = a[1] + 1;
                for(int i = 2; i < n; ++i)
                    if(ans == a[i] - 1)
                        ans = a[i] + 1;
                    else flag = false;
            } else flag = false;
            if(flag) printf("%d\n", ans);
            else puts("-1");
        }
    }
    return 0;
}

### G. 保龄球

Problem Description

Oyk开了一个保龄球馆,馆中有NN个保龄球瓶一字排开,每个保球瓶都有一个固定的权值,它们分别是A1,A2,,ANA_1,A_2,\cdots,A_N。每次掷出一个保龄球只会打倒一个保龄球瓶,假如打倒的是第ii个保龄球瓶,则得到的分数为它与相邻两个没有被倒下的保龄球瓶共三个权重的乘积,即Ai1AiAi+1A_{i-1}*A_i*A_{i+1},然后第i1i-1与第i+1i+1个保龄球瓶就变成相邻的了。(当然,打倒最前或最后的瓶得到的分数只有两个数的乘积,打倒剩下的最后一个瓶得到的分数即为它本身的权重)。现在给你NN个保龄球瓶的权重,问不同的打倒方案中能达到的最高得分是多少?

Input

输入数据的第一行为一个正整数TT,代表着测试数据数量。对于每一个测试数据,第一行为一个正整数NN,代表着保龄球瓶的数量。第二行为NN个正整数A1,A2,,ANA_1,A_2,\cdots,A_N,按顺序表示这NN个保龄球瓶的权重。

0<N<1000<N<100

0<Ai<1000<A_i<100

Output

输出数据总共有TT行,每一行对应一个测试数据。对于每一个测试数据,输出一个正整数,表示能达到的最大总得分。

Sample Input

1  
4  
1 2 2 1

Sample Output

10

解题思路

  • DP。
  • 时间复杂度:O(N3)O(N^3)

参考代码(出题人的代码)

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;

int a[200];
int dp[200][200];

int main() {
    int t, n;
    scanf("%d", &t);
    while(t--) {
        scanf("%d", &n);
        memset(dp, 0, sizeof(dp));
        a[0] = a[n + 1] = 1;
        for(int i = 1; i <= n; ++i) scanf("%d", a + i);
        for(int k = 0; k < n; ++k)
            for(int l = 1; l + k <= n; ++l) {
                int r = l + k;
                for(int mid = l; mid <= r; ++mid)
                    dp[l][r] = max(dp[l][r], dp[l][mid - 1] + a[l - 1] * a[mid] * a[r + 1] + dp[mid + 1][r]);
            }
        printf("%d\n", dp[1][n]);
    }
    return 0;
}

### H. 淹水

Problem Description

某地区有两个山峰。某年由于洪水,高的山峰被淹了两次,而低的山峰只被淹了一次。有可能吗?

答:有可能。在一次洪水淹了高的山峰,退去时低的山峰尚未露出水面,水位就又上升淹了高处山峰。

现在给出若干个山峰的高度和淹没次数,问这个次数可能出现吗?

Input

输入有多组数据,处理到文件尾。每组数据中,第一行为NN,山峰数量,下面NN行分别有两个数字A,BA, B,表示一座高度为AA的山峰被淹了BB次。

0A,B,N<100000 \leq A, B, N < 10000

Output

如果可能,输出YES;否则输出NO。

Sample Input

2
7 2
3 1

Sample Output

YES

解题思路

  • 水题。原定是签到题。
  • 先从小到大排个序,1. 如果前面的山峰没被淹过,后面高的山峰就不可能被淹;2. 如果相同高度的山峰被淹次数不等,则不可能。

参考代码

#include <stdio.h>
#include <algorithm>
using std::sort;

int N;
struct Mountain {
    int height, count;
    bool operator<(const Mountain&c)const {
        return height < c.height;
    }
}mountain[10010];

void read() {
    for (int i = 0; i < N; ++i)
        scanf("%d%d", &mountain[i].height, &mountain[i].count);
}

int judgeEqual(int idx) {
    return mountain[idx].height != mountain[idx-1].height
        || mountain[idx].count == mountain[idx-1].count;
}

int judgeImpossible(int idx) {
    return mountain[idx-1].count == 0 && mountain[idx].count > 0;
}

int work() {
    sort(mountain, mountain+N);
    int eqFlag = 1, impFlag = 0;
    for (int i = 1; i < N; ++i) {
        eqFlag = judgeEqual(i);
        if (!eqFlag) return 0;
        impFlag = judgeImpossible(i);
        if (impFlag) return 0;
    }
    return 1;
}

int main() {
    while (~scanf("%d", &N)) {
        read();
        puts(work()? "YES": "NO");
    }
    return 0;
}



出题及解题总结

复盘

  1. 我没参与出题,因为我太蠢了。
  2. 想要复盘整个过程,也有很多话想说,最终还是按下了无数次退格。
  3. 嗨呀,大家都辛苦了啊!

建议、意见、吐槽

欢迎在下方评论区提出问题,4月内我都会回复and更新。





本文基于知识共享许可协议知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议发布,欢迎引用、转载或演绎,但是必须保留本文的署名BlackStorm以及本文链接http://www.cnblogs.com/BlackStorm/p/SCNUCPC_2017_Solution.html,且未经许可不能用于商业目的。如有疑问或授权协商请与我联系

posted @   BlackStorm  阅读(1187)  评论(0编辑  收藏  举报
编辑推荐:
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
点击右上角即可分享
微信分享提示