代碼地址
GitHub:https://github.com/JiuSiZhang/021700827
9.25更新,修改傳參方式。
PSP表格
PSP2.1 | Personal Software Process Stages | 預估耗時(小時) | 實際耗時(小時) |
---|---|---|---|
Planning | 計划 | 1h | 0.5h |
Estimate | 估計這個任務需要多少時間 | 15h | 12.5h |
Development | 開發 | 1h | 1h |
Analysis | 需求分析 (包括學習新技術) | 3h | 2h |
Design Spec | 生成設計文檔 | 1h | 0.5h |
Design Review | 設計復審 | 1h | 0.5h |
Coding Standard | 代碼規范 (為目前的開發制定合適的規范) | 1h | 0.5h |
Design | 具體設計 | 1h | 1h |
Coding | 具體編碼 | 1h | 1h |
Code Review | 代碼復審 | 1h | 1h |
Test | 測試(自我測試,修改代碼,提交修改) | 1h | 1h |
Reporting | 報告 | 1h | 1h |
Test Repor | 測試報告 | 1h | 1h |
Size Measurement | 計算工作量 | 0.5h | 0.5h |
Postmortem & Process Improvement Plan | 事后總結, 並提出過程改進計划 | 0.5h | 1h |
合計 | 15h | 12.5h |
解題思路
這個題目其實很簡單,本質就是搜索+回溯,我們只要寫一個dfs函數和check函數就應該可以完成了,比較麻煩的可能是環境方面的設置和讀入,話不多說,先上代碼,然后一一解釋。
dfs函數
基本的dfs函數,判斷當前空有沒用被填過,如有,則繼續向下搜索,否則開始填數並進行驗證記得回溯。
void dfs(int x)//dfs函數 遞歸填數
{
if (flag)//如果已經完成 直接返回
{
return;
}
if (x == m * m)//已經找完
{
output();
flag = 1;
return;
}
int r = x / m;//行
int c = x % m;//列
if (!s[r][c])//沒填過就填這個空
{
for (int i = 1; i <= m; i++)
{
if (check(x, i))
{
s[r][c] = i;
dfs(x + 1);
s[r][c] = 0;//回溯
}
}
}
else//已經有數就跳過
{
dfs(x + 1);
}
}
check函數
這個函數的主要作用是check行和列是否合法,並判斷是否為4 6 8 9,因為它們需要驗證宮。
bool check(int x, int val)//第一個驗證函數 驗證行與列且 4 6 8 9時需要判斷宮
{
int r = x / m;
int c = x % m;
for (int i = 0; i<m; i++)//行
{
if (s[r][i] == val)
{
return 0;
}
}
for (int i = 0; i<m; i++)//列
{
if (s[i][c] == val)
{
return 0;
}
}
if (m == 4)//4*4
{
if (check2(x, 2, 2, val))
{
return 1;
}
else
{
return 0;
}
}
else if (m == 6)//6*6
{
if (check2(x, 2, 3, val))
{
return 1;
}
else
{
return 0;
}
}
else if (m == 8)//8*8
{
if (check2(x, 4, 2, val))
{
return 1;
}
else
{
return 0;
}
}
else if (m == 9)//9*9
{
if (check2(x, 3, 3, val))
{
return 1;
}
else
{
return 0;
}
}
return 1;
}
check2函數
這個函數。用來判斷小宮內是否合法。
bool check2(int x, int r, int c, int val)//第二個驗證函數 判斷宮
{
int a = x / m;
int b = x % m;
a = a / r * r;//行
b = b / c * c;//列
for (int i = a; i<a + r; i++)
{
for (int j = b; j<b + c; j++)
{
if (s[i][j] == val)
{
return 0;
}
}
}
return 1;
}
output函數
輸出函數,將結果輸出至文件,注意不一定忽略行末空格。
void output()//輸出函數
{
for (int i = 0; i<m; i++)
{
for (int j = 0; j<m; j++)
{
fprintf(fp2, "%d", s[i][j]);//輸出至文件
if (j < m - 1)
{
fprintf(fp2, " ");
}
}
fprintf(fp2, "\n");
}
}
main函數
注意讀入方式,一開始不知道怎么通過命令行讀入參數,后來看了先寫的同學的博客的百度后才知道的。argv【2】,argv【4】,argv【6】,argv【8】分別對應m,n,輸入文件名,輸出文件名,然后我們定義兩個FILE變量,通過fopen打開文件,再用fscanf讀取數據。輸出到文件用fprintf函數。
int main(int argc, char *argv[])
{
//std::ios::sync_with_stdio(false);
//cin.tie(0);
//m = atoi(argv[2]);//讀取參數
//n = atoi(argv[4]);
//char *inputname = argv[6];
//char *outputname= argv[8];
string a, b, c, d;
a = "-m";
b = "-n";
c = "-i";
d = "-o";
fp1 = NULL;
fp2 = NULL;
for (int i = 1; i < argc; i++)
{
if (argv[i] == c)
{
fp1 = fopen(argv[++i], "r");//打開輸入文件
if (fp1 == NULL)
{
return -1;
}
continue;
}
if (argv[i] == d)
{
fp2 = fopen(argv[++i], "w");//打開輸出文件,清空文件
if (fp2 == NULL) //
{
return -1;
}
continue;
}
if (argv[i] == a)
{
m = argv[++i][0] - '0';
continue;
}
if (argv[i] == b)
{
n = argv[++i][0] - '0';
continue;
}
}
while (n--)
{
mst(s, 0);//初始化
flag = 0;
for (int i = 0; i<m; i++)//輸入
{
for (int j = 0; j<m; j++)
{
fscanf(fp1, "%d", &s[i][j]);//文件讀入
}
}
//fp2 = fopen(outputname, "a");//打開輸出文件
dfs(0); //開始填數
if (n > 0)
{
fprintf(fp2, "\n");//輸出至文件
}
}
if (fp1 != NULL)
{
fclose(fp1);//關閉輸入文件
}
if (fp2 != NULL)
{
fclose(fp2);//關閉輸出文件
}
return 0;
}
難點
其實本次的編程並不難,難的應該是一系列沒有學過的操作,比如文件操作,vs工程操作,github操作,這些需要自己去網上找資料學習,不過一遍之后的確熟悉了很多。
Code Quality Analysis檢查結果
性能分析工具Studio Profiling Tools分析結果
測試結果展示
3*3
4*4
5*5
6*6
7*7
8*8
9*9
全家福
收獲與心路歷程
學到很多東西,以前自己只會打打題目,其他東西都不怎么忙會用,通過這次實驗,學到了很多東西,github,vs,文件操作。以及一些項目管理與測試的知識。