深基刷題記錄(3)

P1219 [USACO1.5]八皇后 Checker Challenge

在這裏插入圖片描述
在這裏插入圖片描述

#include<bits/stdc++.h>
using namespace std;
int a[100],b[100],c[100],d[100];
//a數組表示的是行;
//b數組表示的是列;
//c表示的是左下到右上的對角線;
//d表示的是左上到右下的對角線;
int total;//總數:記錄解的總數
int n;//輸入的數,即N*N的格子,全局變量,搜索中要用
int print()
{
    if(total<=2)//保證只輸出前三個解,如果解超出三個就不再輸出,但後面的total還需要繼續疊加
    {
        for(int k=1;k<=n;k++)
        cout<<a[k]<<" ";//for語句輸出
        cout<<endl;
    }
    total++;//total既是總數,也是前三個排列的判斷
}
void queen(int i)//搜索與回溯主體
{
    if(i>n)
    {
        print();//輸出函數,自己寫的
        return;
    }
    else
    {
        for(int j=1;j<=n;j++)//嘗試可能的位置
        {
            if((!b[j])&&(!c[i+j])&&(!d[i-j+n]))//如果沒有皇后佔領,執行以下程序
            {
                a[i]=j;//標記i排是第j個
                b[j]=1;//宣佈佔領縱列
                c[i+j]=1;
                d[i-j+n]=1;
                //宣佈佔領兩條對角線
                queen(i+1);//進一步搜索,下一個皇后
                b[j]=0;
                c[i+j]=0;
                d[i-j+n]=0;
                //(回到上一步)清除標記
            }
        }
    }
}
int main()
{    
    cin>>n;//輸入N*N網格,n已在全局中定義
    queen(1);//第一個皇后
    cout<<total;//輸出可能的總數
    return 0;
}

P1605 迷宮

在這裏插入圖片描述

#include<bits/stdc++.h>
using namespace std;
int n,m,k,x,y,a,b,ans;
int dx[4] = {0,0,1,-1},dy[4] = {1,-1,0,0};
bool vis[6][6];
struct Node{
    int x,y,used[6][6];
}sa;
int main()
{
    scanf("%d%d%d",&n,&m,&k);
    scanf("%d%d%d%d",&x,&y,&a,&b);         //雖然這裏可以合併成一個句子,但是由於我是從python轉過來的,建議大家以後寫代碼都設置一個界限,代碼不宜太長
    for(int i = 1,aa,bb;i <= k; i++)     //大家注意一下,我現在是直接把變量定義在循環裏面。
    {
        scanf("%d%d",&aa,&bb);           //現在我們不能走障礙了。
        vis[aa][bb] = 1;
    }
    queue<Node> q;      //定義結構體後,可以直接使用結構體名定義變量或者隊列。
    sa.x = x;
    sa.y = y;         //橫縱座標替換,這樣寫起來方便。
    sa.used[x][y] = 1;//標記走過的路徑
    q.push(sa);
    while(!q.empty())
    {
        Node now = q.front();     //一起拿出來
        q.pop();
        for(int i = 0;i < 4; i++)
        {
            int sx = now.x + dx[i];
            int sy = now.y + dy[i];
            if( now.used[sx][sy] 
                || vis[sx][sy] 
                || sx == 0 || sy == 0 
                || sx > n || sy > m)
                continue;    //如果這裏走過,或者這裏是障礙,或者這裏是牆壁,那麼這裏就不能走。
            if(sx == a && sy == b)
            {
                ans++;           //如果這裏是終點,那麼結果數量加一
                continue;
            }
            sa.x = sx;
            sa.y = sy;
            memcpy(sa.used,now.used,sizeof(now.used));
            sa.used[sx][sy] = 1;     //這裏的操作都是爲了標記路徑
            q.push(sa);
        }
    }
    printf("%d",ans);
    return 0;
}

bfs 正確代碼的習慣:
1、bfs 不寫 check
2、寫兩個 if,第一個相當於 check,後面跟 continue
3、很少寫 q.push((node){a,b}) ,這樣的省略寫法
4、用 dx 和 dy 兩個數組表示上下左右
關於此題:
在結構體中用了記錄路徑的數組防止 TLE

P1019 單詞接龍

在這裏插入圖片描述

#include<bits/stdc++.h>
using namespace std;
const int N=30;
int chu[N];
string t[N],str;
int n,maxx;
int gao(string x,string q)//找是否可以相連 
{
	for(int i=x.size()-1;i>=0;i--)//倒的找找到一個就退出這樣肯定是最優的 
	{
		if(x[i]==q[0])//如果和匹配開頭一樣就開始找是否合法 
		{
			int l=i;//當前開始的位置 
			for(int j=0;j<q.size();j++){if(x[l]==q[j])l++;else break;}//如果一樣的話就一直找否則就退出來,必須退! 
			if(l==x.size())return l-i;//如果找完了就算出有多少個是匹配的這個就是重複的值 
		}
	}
	return 0;//沒有就返回0回去;
}
void dfs(int fu,string x)
{
	maxx=max(maxx,(int)x.size()-fu);//用字符的長度減去重複的長度 
	for(int i=1;i<=n;i++)//找接在後面的單詞 
	{
		int duan=0;
		if(chu[i]==2)continue;//一個單詞可以用兩次,如果用過兩次就直接跳
		duan=gao(x,t[i]);//duan爲他們重疊的長度 
		if(!duan)continue;//如果爲0沒重疊就直接跳過(很重要!) 
		chu[i]++;//這個位置標記一下表示用過 
		dfs(fu+duan,x+t[i]);//重疊的部分相加字符串更新 
		chu[i]--;//回溯 
	}
	return ;//沒找有一個匹配就返回 
}
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)cin>>t[i];//把每個都存起來 
	cin>>str;
	dfs(0,str);
	cout<<maxx;//最大長度 
	return 0;
}

注意感嘆號部分,那些部分是必須加的(而不是爲了省時間)

P1101 單詞方陣

在這裏插入圖片描述
在這裏插入圖片描述

#include <bits/stdc++.h>
using namespace std;

const int maxn=100+10;
struct node
{
    int x,y;
}c[maxn];//記錄路徑
char fz[maxn][maxn],stand[]="yizhong";//fz保存單詞矩陣,stand保存保準的“yizhong”便於匹配
int vis[maxn][maxn];//保存路徑,是否該點爲答案
int dir[][2]={{-1,-1},{-1,0},{-1,1},{0,-1},{0,1},{1,-1},{1,0},{1,1}};//八向的常量數組
void dfs(int x,int y,node c[],int k,int cur)
{
    if(cur==7){
        for(int i=0;i<7;i++)
            vis[c[i].x][c[i].y]=1;
    }
    else{
        int dx=x+dir[k][0];//沿着正確的k方向搜索
        int dy=y+dir[k][1];
        if(cur==6||fz[dx][dy]==stand[cur+1]){//當cur是6的時候就已經完成了(因爲單詞是7個字母) 
            c[cur].x=x;c[cur].y=y;
            dfs(dx,dy,c,k,cur+1);
        }
    }
}
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=0;i<n;i++)
        scanf("%s",fz[i]); //一次存儲一行 
    memset(vis,0,sizeof(vis));
    for(int i=0;i<n;i++)//搜索y,i相連的可能的方向k,以k爲方向進行DFS
        for(int j=0;j<n;j++)
            if(fz[i][j]=='y')
				for(int k=0;k<8;k++)
				{
                    int x=i+dir[k][0];
                    int y=j+dir[k][1];
                    if(fz[x][y]=='i')
                        dfs(i,j,c,k,0);//注意是從 i j 開始 
                }
    for(int i=0;i<n;i++){//輸出結果
        for(int j=0;j<n;j++)
            if(vis[i][j]) printf("%c",fz[i][j]);
            else printf("*");
        printf("\n");
    }
    return 0;
}

dfs 正確代碼的習慣:
1、寫 dir 二維數組表示方向
關於此題:
1、此代碼定義了結構體數組,用於記錄路徑
2、之所以先找 y 和 i 是因爲要用 k 確定方向(也可以不用先找 y 和 i )
3、字符串一次存一行

P1443 馬的遍歷

在這裏插入圖片描述

#include<bits/stdc++.h>	
using namespace std;
struct xy{
    int x,y;
}node,Top;
const int dx[4]={1,-1,2,-2};
const int dy[4]={1,-1,2,-2};//雖說一共16個方向 但是在程序中有具體判斷
int a[401][401];
bool b[401][401]; 
int n,m;
int main(){
    memset(b,true,sizeof(b));
    memset(a,-1,sizeof(a));
    int x,y;
    scanf("%d%d%d%d" ,&n ,&m ,&x ,&y );
    a[x][y] = 0;
    b[x][y] = false;
    queue<xy> Q;//構建隊列
    node.x = x;
    node.y = y;
    Q.push(node);//起始點入隊
    while (!Q.empty()){
        Top=Q.front();//取出隊首點
        Q.pop();//隊首點出隊
            for (int i=0;i<4;i++)
                for (int j=0;j<4;j++)
                    if (abs(dx[i])!=abs(dy[j])){//判斷方向
                        int NewX=Top.x+dx[i];
                        int NewY=Top.y+dy[j];
                        if (NewX<1||NewX>n||NewY<1||NewY>m) continue;//判斷越界
                        if (b[NewX][NewY]){//使用布爾數組保證每個點只入隊一次 時間複雜度明顯低於DFS
                            node.x=NewX;
                            node.y=NewY;
                            Q.push(node);
                            b[NewX][NewY] = false;//標記已入隊
                            a[NewX][NewY] = a[Top.x][Top.y]+1;//路徑+1
                        }
                    }
    }
    for (int i=1;i<=n;i++){
        for (int j=1;j<=m;j++)
            printf("%-5d", a[i][j]);//-5d是向左對其,並有 5格 
        printf("\n");
    }
    return 0;
}

重點提醒:
不能這樣寫:bool b[405][405]={true};
要這樣寫:fill(b[0],b[0]+401*401,true);
因爲二維數組是不能直接用 true

P2895 [USACO08FEB]Meteor Shower S

在這裏插入圖片描述
在這裏插入圖片描述

#include<bits/stdc++.h>
using namespace std;
struct node
{
	int x,y,time;
} p; //x,y存座標,time存當前的時間 
int m,x,y,t,nx,ny,time1[305][305],c[305][305]; //time1數組存這個格子流星最早到達的時間,c存是否到過這個格子,數組開大點!!!! 
int b1[4]= {0,0,1,-1},b2[4]= {1,-1,0,0};
queue<node>q;
int main()
{
	cin>>m;
	for(int i=0; i<=302; i++)
		for(int j=0; j<=302; j++)
			time1[i][j]=-1; //先都賦初值爲-1 
	for(int i=1; i<=m; i++)
	{
		cin>>x>>y>>t;
		if(t<time1[x][y]||time1[x][y]==-1) //這顆流星到達的時間必須小於前面流星或焦土到達的時間,或者還暫時沒有流星及焦土 
		time1[x][y]=t; 
		for(int i=0; i<4; i++)
		{
			nx=x+b1[i],ny=y+b2[i];
			if(nx>=0&&ny>=0&&(time1[nx][ny]==-1||t<time1[nx][ny])) //注意是 t,不是 t+1 !
				time1[nx][ny]=t;  //枚舉焦土 
		}
	}
	p.x=0,p.y=0,p.time=0,c[0][0]=1;
	q.push(p);  
	while(!q.empty())
	{
		p=q.front();q.pop();
		for(int i=0; i<4; i++)
		{
			nx=p.x+b1[i],ny=p.y+b2[i];
			if(nx>=0&&ny>=0&&c[nx][ny]==0&&(time1[nx][ny]==-1||p.time+1<time1[nx][ny])) //沒有流星到過或者bessie到這個格子的時候流星還沒有到達 
			{
				node txt;
				txt.x=nx,txt.y=ny,txt.time=p.time+1,c[nx][ny]=1; //擴展節點 
				q.push(txt);
				if(time1[nx][ny]==-1) //判斷當前的格子是否安全 
				{
					cout<<txt.time<<endl; //輸出答案 
					return 0;
				}
			}
		}
	}
	cout<<-1<<endl; //到不了安全的格子就輸出-1 
	return 0;
}

加了時間的因素,很強

P2404 自然數的拆分問題

在這裏插入圖片描述
在這裏插入圖片描述

#include<bits/stdc++.h>	
using namespace std;
int n, p[11]={1}, m;
void print(int a)
{
	for(int i=1; i<a; i++)
		cout<<p[i]<<"+";
	cout<<p[a]<<endl;
}
void dfs(int a)
{
	for(int i=p[a-1]; i<=m; i++)//回溯後跳出分支
	{
		if(i==n) return;//防止最後一行輸出n
		p[a]=i;
		m-=i;
		if(m==0) print(a);//m減完時,該方案已排列完畢,進行輸出
		else dfs(a+1);//否則繼續搜索
		m+=i;//回溯
	}
}
int main()
{
	cin>>n;
	m=n;
	dfs(1);
	return 0;
}

又是在 for 循環初始化數組,這種回溯思路(dfs)非常難想,包括我現在都還沒想懂

P1596 [USACO10OCT]Lake Counting S

在這裏插入圖片描述
在這裏插入圖片描述

//第一種方法:BFS
#include<cstdio>
#include<queue>
using namespace std;
const int maxn = 1e8 + 100;
struct queue
{
    int l = 0,r = 0,a[maxn];
    void push(int x){
        a[r++] = x;
    }
    int front(){
        return a[l];
    }
    void pop(){
        l++;
    }
    int empty(){
        return l >= r ? 1 : 0;
    }
}hori,para;//行的隊列 列的隊列
int n,m;
int ans;
char s[101][101];
inline int read(){//讀入優化,可以加快數字的輸入
    char p=0;int r=0,o=0;
    for(;p<'0'||p>'9';o|=p=='-',p=getchar());
    for(;p>='0'&&p<='9';r=(r<<1)+(r<<3)+(p^48),p=getchar());
    return o?(~r)+1:r;
}
inline void bfs(int x,int y){//不用遞歸時可以加inline,提高1ms的運行速度
    s[x][y]='.';
    int dx,dy;
    for(int i=-1;i<=1;i++){
        for(int j=-1;j<=1;j++){
            dx=x+i;
            dy=y+j;
            if(dx>=0&&dx<n&&dy>=0&&dy<m&&s[dx][dy]=='W'){
                hori.push(dx);
                para.push(dy);
            }
        }
    }
}
int main(){
    n=read();m=read();//看不懂的話可以把這一行改成cin或scanf
    for(int i=0;i<n;i++){
        scanf("%s",s[i]);
    }
    for(int i=0;i<n;i++){
        for(int j=0;j<m;j++){
            if(s[i][j]=='W'){
                hori.push(i);
                para.push(j);
                while(!hori.empty()){//如果隊列不爲空,一個就行了 
                    bfs(hori.front(),para.front());//廣搜隊列前面的元素
                    hori.pop();para.pop();//彈出元素
                }
                ans++;
            }
        }
    }
    printf("%d",ans);
    return 0;
}


//第二種方法:DFS
#include <bits/stdc++.h>
using namespace std;
char a[101][101];
int ans;
int n,m;
void dfs(int x,int y){
    a[x][y]='.';
    int dx,dy;
    for(int i=-1;i<=1;i++){
        for(int j=-1;j<=1;j++){
            dx=x+i;
            dy=y+j;
            if(dx>=0&&dx<=n&&dy>=0&&dy<m&&a[dx][dy]=='W'){
                dfs(dx,dy);
            }
        }
    }
    return;
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=0;i<=n;i++){
    	scanf("%s",a[i]);//避免換行帶來問題這裏直接讀入字符串
    }
    for(int i=0;i<=n;i++){
        for(int j=0;j<m;j++){
            if(a[i][j]=='W'){//如果是W的話就直接開始遍歷
                dfs(i,j);
                ans++;//水潭加一處
            }
        }
    }
    printf("%d",ans);
    return 0;
}

關於此題 BFS:
1、雙重 for循環代替 dir方向數組
2、雙重 queue存儲座標
3、手寫隊列
4、BFS 不是遞歸
5、既然不是遞歸就加上 inline,可以提高運行速度
關於此題 DFS:
和 BFS 差不多,就是遞不遞歸的區別

P1162 填塗顏色

在這裏插入圖片描述
在這裏插入圖片描述

#include <bits/stdc++.h>
using namespace std;
int a[32][32],b[32][32];
int dx[5]={0,-1,1,0,0};
int dy[5]={0,0,0,-1,1};//第一個表示不動,是充數的,後面的四個分別是上下左右四個方向
int n,i,j;
void dfs(int p,int q){
    int i;
    if (p<0||p>n+1||q<0||q>n+1||a[p][q]!=0) return;//如果搜過頭或者已經被搜過了或者本來就是牆的就往回
    a[p][q]=1;//染色
    for (i=1;i<=4;i++) dfs(p+dx[i],q+dy[i]);//向四個方向搜索
}
int main(){
    cin>>n;
    for (i=1;i<=n;i++)
        for (j=1;j<=n;j++){
            cin>>b[i][j];//其實不拿兩個數組也可以,不過我喜歡啦
            if (b[i][j]==0) a[i][j]=0;
            else a[i][j]=2;
        }
    dfs(0,0);//搜索 從0,0開始搜
    for (i=1;i<=n;i++){
        for (j=1;j<=n;j++)
        if (a[i][j]==0) cout<<2<<' ';//如果染過色以後i,j那個地方還是0,說明沒有搜到,就是周圍有牆,當然就是被圍住了,然後輸出2
        else cout<<b[i][j]<<' ';//因爲被染色了,本來沒有被圍住的水和牆都染成了1,所以就輸出b[i][j]
        cout<<'\n';//換行
    }
}

從邊界(0,0)開始搜索,因爲與邊界連通的塊是不閉合的塊,輸入的塊是從(1,1)開始的

P1032 字串變換

在這裏插入圖片描述
在這裏插入圖片描述

#include <iostream>
#include <string>
#include <cstring>
#include <queue>
#include <map>
#define maxn 15
using namespace std;
struct node{//方便搜索,也可以使用pair簡化
    string str;
    int step;
}s,v;

string a,b;
string orginal[maxn];
string translated[maxn];
int n,ans;
map<string,int> ma;//很重要的東西,用來判重,否則會TLE在第3點和第5點

string trans(const string &str,int i,int j){//最好加上 const 
    string ans = "";
    if (i+orginal[j].length() > str.length())//位置 + 原字符串的長度加大於要轉換的字符串 
        return ans;

    for (int k=0; k < orginal[j].length();k++)
        if (str[i+k] != orginal[j][k])
            return ans;

    ans = str.substr(0,i);
    ans+=translated[j];
    ans+=str.substr(i+orginal[j].length());
    return ans;
}

void bfs(){//一個平淡無奇的bfs過程 
    queue <node> q;
    s.str = a;
    s.step = 0;
    q.push(s);

    while (!q.empty()){
        node u = q.front();
        q.pop();
        string temp;

        if(ma[u.str] == 1) //剪枝,判斷重複的路徑
            continue;

        if (u.str == b){
            ans = u.step;
            break;
        }
        ma[u.str] = 1;
        for (int i=0;i < u.str.length();i++)//枚舉當前串所有可能位置
            for (int j=0; j<n; j++){//枚舉所有可能手段
                temp = trans(u.str,i,j);
                if (temp != ""){
                    v.str = temp;
                    v.step = u.step+1;
                    q.push(v);
                }
            }
    }
    if (ans > 10 || ans == 0)
        cout << "NO ANSWER!" << endl;
    else
        cout << ans << endl;

}

int main(){
    cin >> a >> b;
    while (cin >> orginal[n] >> translated[n])
        n++;
    bfs();
    return 0;
}

由題目最小步數,知道要用廣搜
補充:此題代碼只能直接上交評測機,不能運行,因爲 while 是無窮無盡的

P1825 [USACO11OPEN]Corn Maze S

在這裏插入圖片描述
在這裏插入圖片描述

#include<bits/stdc++.h>
using namespace std;
int a[500][500],qx,qy,zx,zy,cz1,cz2,bj[500][500],n,m;
int d[4][2]= {{-1,0},{1,0},{0,-1},{0,1}};
struct node
{
	int x;
	int y;
	int bs;
} ans[101010];
int cz(int x,int y)
{
	for(int i=1; i<=n; i++)
	{
		for(int j=1; j<=m; j++)
		{
			if(!(i==x&&j==y))//注意不能與原點相同
			{
				if(a[i][j]==a[x][y])//直接判斷匹配
				{
					cz1=i;
					cz2=j;
					return 0;
				}
			}
		}
	}
}
int bfs()
{
	int head=0,tail=1; //用這個代替了隊列 
	do
	{
		head++;
		if(a[ans[head].x][ans[head].y]>='A'&&a[ans[head].x][ans[head].y]<='Z')//判斷是不是腳踩傳送門
		{
			cz(ans[head].x,ans[head].y);//找到對應的傳送門座標
			ans[head].x=cz1;
			ans[head].y=cz2;
		}
		for(int i=0; i<4; i++)
		{
			int xx=ans[head].x+d[i][0];
			int yy=ans[head].y+d[i][1];
			if(a[xx][yy]!=0&&bj[xx][yy]==0)//廣搜
			{
				bj[xx][yy]=1; //不需要回頭,故直接標記爲 1,此路不再走 
				tail++;
				ans[tail].x=xx;
				ans[tail].y=yy;
				ans[tail].bs=ans[head].bs+1;
				if(xx==zx&&yy==zy)//判目標
				{
					printf("%d\n",ans[tail].bs);
					return 0;
				}
			}
		}
	}
	while(head<tail);
}
int main()
{
	char s;
	scanf("%d%d",&n,&m);
	for(int i=1; i<=n; i++)
	{
		for(int j=1; j<=m; j++)
		{
			cin>>s;//輸入預處理,預處理的特別棒 
			if(s=='.')
			{
				a[i][j]=1;
			}
			if(s>='A'&&s<='Z')
			{
				a[i][j]=s;//這裏是一個很重要的點,等下說明
			}
			if(s=='@')
			{
				qx=i;
				qy=j;
				a[i][j]=1;
			}
			if(s=='=')
			{
				zx=i;
				zy=j;
				a[i][j]=1;
			}
		}
	}
	bj[qx][qy]=1;//注意一定要標記起點
	ans[1].x=qx;
	ans[1].y=qy;
	bfs();
	return 0;
}

意料之外,情理之中的神奇思路…
關於此題 BFS:
1、用 tail 和 head 和 數組(加上 do while )代替了隊列
2、BFS 不走回頭路(迷宮一類問題)
3、直接用數字(ascii 碼)代替了輸入的字符

支票面額

一個採購員去銀行兌換一張 y 元 f 分的支票,結果出納員錯給了 f 元 y 分。採購員用去了 n 分之後才發覺有錯,於是清點了餘額尚有 2y 元 2f 分,問該支票面額是多少?

輸入格式:
輸入在一行中給出小於100的正整數 n

輸出格式:
在一行中按格式 y.f 輸出該支票的原始面額。如果無解,則輸出 No Solution

輸入樣例1:
23
輸出樣例1:
25.51
輸入樣例2:
22
輸出樣例2:
No Solution

//第一種方法:枚舉
# include <stdio.h>

int main(void) {
	int n, y, f;
	int i, j;
	int is=0;
	
	scanf ("%d", &n);
	
	for(i=0; i<100; i++) {
		for (j=0; j<=100; j++) {
			if (n == 98*i-199*j) {
				f = i;
				y = j;
				is = 1;
				break;
			}
		}
		if (1 == is) break;
	}
	
	if (0 == is) {
		printf("No Solution");
	} else {
		printf("%d.%d", y, f);
	}
	
	return 0;
}

//第二種方法:列表達式
#include<cstdio>
#include<iostream>
using namespace std;
 
int y,f,n;
 
int main(){
scanf("%d",&n);
y=(98-n)/3;
f=2*y+1;
 
if((98-n)%3==0) printf("%d.%d\n",y,f);      
else{
    for(int i=0;i<100;i++){
        for(int j=0;j<100;j++){
            if((2*i*100+2*j)==(j*100+i-n)){
                printf("%d.%d\n",i,j);
                return 0;
            }
        }
    }
    printf("No Solution\n");
}
 
return 0;
}

第一種方法不解釋
第二種方法:
首先,按照正常邏輯思維來說,我們會討論兩種情況:

1.y>n 這就意味着,我們可以推出一組方程(兩個等式):

f==2y;

y-n==2f;

但是,很顯然,我們可以得到這個方程是負數解,錢不可能是負的,所以(按照正常想法),我們自然可以得出一個結論:y要比n小,也就是第二種情況是必然成立的。

2.y<n 這也意味着,我們可以推出一組方程(兩個等式):

f-1==2y;

100+y-n==2f;

按照正常想法,我們自然可以解出方程:y=(98-n)/3;f=2*y+1;

所以我會這樣寫:scanf一個n之後,if((98-n)%3!=0),那我們就可以直接判斷 No solution了,因爲錢的單位不管是元還是分,都不可能是小數,必然是整數。然而,然而…

第一個測試點就是不對。

所以,到底錯在哪裏呢?

這就要回到我們第二種情況最開始的那兩個方程,當我們用掉n分之後,所剩下的2y元和2f分真的和最初的錢數有那樣的一一對應的關係嗎?

並不是,仔細想想平常生活中用現金買東西,我們會發現最後手裏剩下的零錢是一大把的,也就是說,在銀行給我們錢之後,我們沒有花錢之前,我們可以保證,y分的範圍一定是小於100的,但是當我們花掉n分錢後,因爲在這期間有可能會產生找零的各種情況,我們最後所剩下的2f分錢有可能就是大於100的,舉個栗子:n == 97,最開始我們有68元,33分,花掉97分之後,我們剩下66元,136分(我們可以統一到分爲單位 驗證一下),那麼在這種情況裏,我們就不能簡單地寫做:f-1==2y; 100+y-n==2f;

不然這會導致看似 No solution,其實是有解的情況。

我猜,這就是第一個測試點卡我的原因。

寫代碼時候,我們可以直接用兩個for循環完事兒,就不用再管(98-n)%3是否爲0,但是我覺得代碼按照一定邏輯來寫也是可以的,雖然看起來冗餘了一些。先驗證(98-n)%3是否爲0,如果爲0,直接算出來y和f;如果不爲0,再用兩個for循環去尋找我們的y和f,也就是暴力搜索。如果這都找不到,那就是 No solution了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章