ACM競賽之輸入輸出(以C與C++為例)


本文轉自互聯網,內容、排版有修正。

歡迎和大家交流技術相關問題:
郵箱: jiangxinnju@163.com
博客園地址: http://www.cnblogs.com/jiangxinnju
GitHub地址: https://github.com/jiangxincode
知乎地址: https://www.zhihu.com/people/jiangxinnju

在ACM程序設計競賽中,一道題目的所有測試數據是放在一個文本文件中,選手將一道題目的程序提交給評判系統運行,程序從該文件中讀取測試數據,再把運行結果輸出到另一個文本文件中。系統把輸出文件與標准答案比對,來評判程序編寫得正確與否。ACM現場賽采用的輸入輸出形式有(1)文件輸入、標准輸出;(2)文件輸入、文件輸出;(3)標准的輸入輸出。而Web形式的ACM程序設計在線評判系統一般采用標准的輸入輸出,但輸入結束有文件末尾標識(EOF),這可以用於確定輸入結束。

一、四種基本輸入形式

1. 一組輸入數據

示例:整數求和
http://acm.njupt.edu.cn/acmhome/problemdetail.do?&method=showdetail&id=1001

C語法:

#include <stdio.h>
int main()
{
    int a,b;
    scanf("%d %d",&a, &b);
    printf("%d\n",a+b);
}

注意:輸入前不要打印提示信息。輸出完畢后立即終止程序,不要等待用戶按鍵。

C++語法:

#include<iostream>
using namespace std;
int main()
{
    int a ,b;
    cin>>a>>b;
    cout<<a+b<<endl;
    return 0;
}

2. 多組輸入數據,不說明多少組,直到讀至輸入文件末尾為止

示例:A + B Problem (1)
http://acm.njupt.edu.cn/acmhome/problemdetail.do?&method=showdetail&id=1084

C語法:

#include <stdio.h>
int main()
{
    int a,b;
    while (scanf("%d %d",&a, &b) != EOF)
        printf("%d\n",a+b);
}

說明:scanf函數返回值就是讀出的變量個數,如:scanf( “%d %d”, &a, &b );如果只有一個整數輸入,返回值是1,如果有兩個整數輸入,返回值是2,如果一個都沒有,則返回值是EOF。EOF是一個預定義的常量,等於-1

C++語法:

#include<iostream>
using namespace std;
int main()
{
    int a ,b;
    while (cin>>a>>b)
        cout<<a+b<<endl;
    return 0;
}

cin是一個對象,表達式cin >> m >> n在讀入發生錯誤返回0,否則返回cin的地址。

3. 多組輸入數據,不說明多少組,以某特殊輸入為結束標志。

示例:A + B Problem (2)
http://acm.njupt.edu.cn/acmhome/problemdetail.do?&method=showdetail&id=1085

C語法:

#include <stdio.h>
int main()
{
    int a,b;
    while(scanf("%d %d",&a, &b) &&(a||b))
         printf("%d\n",a+b);
}

C++語法:

#include<iostream>
using namespace std;
int main()
{
    int a ,b;
    while(cin>>a>>b&&(a||b))
    {cout<<a+b<<endl;}
    return 0;
}

4. 多組輸入數據,開始輸入一個N,接下來是N組數據

示例:A + B Problem (3)
http://acm.njupt.edu.cn/acmhome/problemdetail.do?&method=showdetail&id=1086

C語法:

#include<stdio.h>
int main()
{
    int a ,b,n;
    scanf("%d",&n);
    while(n--)
    {
        scanf("%d %d",&a, &b);
        printf("%d\n",a+b);
    }
    return 0;
}

C++語法:

#include<iostream>
using namespace std;
int main()
{
    int a ,b,n;
    cin>>n
    while(n--)
    {
     cin>>a>>b;
     cout<<a+b<<endl;
    }
    return 0;
}

二、字符串輸入

對字符串的輸入分三種情況:

1、每個字符串中不含空格、制表符及回車

這種情況,用scanf("%s",str)是再好不過的了,比如,測試數據中只有兩個字符串:abc def。要讀入abc與def,可以這樣寫:

char str1[1000], str2[1000];
scanf("%s%s", str1, str2);

2、字符串中含有空格、制表符,但不含回車

對於這種情況,scanf("%s",str)無能為力,因為scanf用空格、制表符及回車作為字符串的分界符。對於一個含有空格、制表符及回車的字符串,如果用scanf("%s",str)來讀,將讀到若干個字符串,這個字符串被scanf分開了。可以用另外一個函數gets。gets函數用回車作為字符串的分界符,比如,有以下的一個字符串:Hello world!。要讀入這個字符串,這樣寫:

char str[1000];
gets(str);

這樣,str的內容就是"Hello world!"了。另外,gets返回NULL表示出錯或end of file。

3、字符串中含回車

在這種情況下,如果沒有題目的說明,程序無法知道哪里是字符串的分界。那么,用scanf("%c",&ch)來讀,一邊讀,一邊判斷分界條件是否滿足,如果滿足,則把當前讀到的東西存到一個字符串中。

三、輸出處理

在初次接觸ACM程序設計競賽時,可能認為:樣例中都是輸入數據和輸入數據在一起,輸出結果和輸出結果在一起,可能會開個數組,把每組的結果存起來,等輸入完了再一起輸出。當遇到不知有多少組測試數據的題,就難以處理了。其實在ACM程序設計競賽中,輸入數據和輸出數據是分別在兩個不同的文件中進行的,程序的輸入和輸出是相互獨立的,所以讀入一組數據就輸出一組結果,跟先讀入所有數據再輸出所有的結果,效果是完全一樣的。因此,每當處理完一組測試數據,就應當按題目要求進行相應的輸出操作。而不必將所有結果儲存起來一起輸出。在處理輸出時,一般要注意:每行輸出均以回車符結束,包括最后一行。

1. 關於空行(Blank line)

很多題目都要求在輸出數據的恰當位置加空行。一個空行就是一個單獨的"\n"。這里,有的題目說:“After each test case, you should output one blank line”,而有的題目說:“Between each test case, you should ouput one blank line”。要注意After和Between的區別,因為如果多了一或少了空行,將導致Presentation Error甚至Wrong Answer。

(1)After
這種情況最簡單,只需要輸出結果后,再加一個printf("\n")或puts("")就行了,就像這樣:

int i;
for (i = 0; i < 10; i++)
{
    printf("%d\n", a);
    printf("\n");
}

示例:A + B Problem (4)
http://acm.njupt.edu.cn/acmhome/problemdetail.do?&method=showdetail&id=1087

C語法:

#include<stdio.h>
int main()
{
    int n,sum,a;
    while(scanf("%d",&n) && n)
    {
         sum=0;
         while(n--)
         {
             scanf("%d",&a);
             sum+=a;
         }
         printf("%d\n",sum);
printf("\n");
    }
    return 0;
}

C++語法:

#include<iostream>
using namespace std;
int main()
{
    int n,sum,a;
    while(cin>>n&&n)
    {
        sum=0;
        while(n--)
        {
            cin>>a;
            sum+=a;
        }
        cout<<sum<<endl;
        cout<<endl;
    }
    return 0;
}

(2)Between
Between和After不同的是,最后一組結果后面不應該再加單獨的"\n",應該像這樣:]

int i;
for (i = 0; i < 10; i++)
{
    printf("%d\n", a);
    if (i != 9)
        printf("\n");
}

由於有時候我們並不知道測試數據有幾組(比如測試數據是以end of file 結束的),用上面的方法就不行了,於是,可以換一種寫法:

int a;
bool bFirst = true;
while (scanf("%d", &a) == 1)
{
    if (!bFirst)
        puts("");
    else
        bFirst = false;
    printf("%d\n", a);
}

這樣,從第二組測試數據起,在輸出每組測試數據的結果之前就會輸出一個空行,和想要的效果是一樣的。

2、關於空格、逗號以及其他分隔符

這種情況與空行的情況相當相似,處理方法也是一樣的,只不過把"\n"改成相應的分隔符就行了。

3、帶格式的字符串輸出

有些題目要求輸出這樣的字符串:abc*****de****f,其中“*”代表空格。
要求是這樣的:str1在前5個字符中左對齊,str2在第6到第10個字符中右對齊,str3在第11到第15個字符中右對齊。
可行的做法是,先初始化一個數組,用' '(空格)填充,再在相應的位置填相應的內容。用程序來表述:

01:char str[1000];
02:char str1[] = "abc", str2[] = "de", str3[] = "f";
03:memset(str, ' ', 1000 * sizeof(char));
04:sprintf(str, "%s", str1);
05:str[strlen(str1)] = ' ';
06:sprintf(str + 5, "%5s", str2);
07:str[10] = ' ';
08:sprintf(str + 10, "%5s", str3);
09:str[15] = '\0';
10:puts(str);

關鍵的部分:
(1)在調用sprintf后,要清除不恰當字符串結束符(第5,7行);
(2)在恰當的位置添加字符串結束符(第9行)。

4、二維數組的輸出

首先要考慮的是數組是按行排列還是按列排列,如果是按行排列,就應該這樣寫:

int i, j;
01: for (i = 0; i < nRow; i++)
02: {
03:     for (j = 0; j < nCol; j++)
        {
            if (j > 0)
                printf(" ");
            printf("%d", a[j]);
        }
        puts("");
    }

如果是按列,就要把1行和3行交換。

5、模擬屏幕輸出

在一些模擬題中,題目要求輸出一幅畫,只不過這個畫是由字符組成的。對於這種情況,可以采用和帶格式的字符串輸出相似的方法,先開一個字符數組(在這里,是二維數組),然后把數組當成屏幕輸出,屏幕的(i, j)點就是數組的(i, j)號元素。最后,輸出這個二維數組就行了。一般來說,可以輸出一個二維字符數組的方法和輸出一般數組的方法是一樣的,用雙重循環來做。不過,可以只用一個循環就可以了,原因是在數組每行的恰當位置(一般是末尾)加了一個'\0',那么,數組的每一行就成了一個字符串,於是,輸出程序就變成了:

int i;
char str[100][100];
...
for (i = 0; i < nRow; i++)
puts(str);


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM