資訊專題

20516林子鈞

鄰國不同色

題目敘述

給一張可以表示國家位置的圖
要用盡量少的顏色把國家著色
(只有上下左右是相鄰)

輸入格式

30*30的矩陣
0為沒有國家
其他的為國家編號(1到99之間)

範例:

輸出格式

以小寫英文字母來表示顏色

輸出30*30的矩陣代表地圖上國家的顏色

不存在國家時輸出'

範例:

作法

  1. 從輸入的圖建立相鄰關係
  2. 用dfs從任一點開始著色
  3. 每次著色時著未相鄰的最小顏色
  4. 繼續dfs相鄰的未著色點

dfs

dfs

void dfs(int pos){
	visited[pos]=1;
	for(auto i:graph[pos]){
    	if(visited[i]==0)dfs(pos);
    }
}
int ma[30][30];//存圖 
set<int> gra[100];//存鄰接關係 
int color[100]={},iru[100]={},cnt=0;//color:存國家對應顏色 iru:存國家編號是否存在 cnt:存國家數 
void dfs(int pos){
	set<int> se;//存鄰國顏色 
	for(auto i:gra[pos]){//遍歷鄰國,紀錄顏色 
		if(color[i])se.insert(color[i]);
	}
	int i=1;auto ind=se.begin();
	for(;i<=se.size();i++){
		if((*ind)!=i&&color[pos]==0)color[pos]=i;//找出不在鄰國中的最小顏色 
		ind++;
	}
	if(color[pos]==0)color[pos]=i;
	for(auto i:gra[pos]){
		if(color[i]==0)dfs(i);//遍歷未著色鄰國 
	}
}
int main(){
	for(int i=0;i<30;i++){
		for(int j=0;j<30;j++){
			char a,b;scanf("%c",&a);scanf("%c",&b);//輸入 
			if(b>='0'&&b<='9')ma[i][j]=(a-'0')*10+b-'0';
			else ma[i][j]=a-'0';
			if(ma[i][j]==0)continue;
			if(iru[ma[i][j]])ma[i][j]=iru[ma[i][j]];//已編號國家直接存 
			else iru[ma[i][j]]=(++cnt),ma[i][j]=iru[ma[i][j]];//未編號國家拿新編號 
		}
	}
	for(int i=0;i<30;i++){
		for(int j=0;j<30;j++){//建立鄰接關係 
			if(i!=0){
				if(ma[i-1][j]!=0&&ma[i][j]!=0)gra[ma[i][j]].insert(ma[i-1][j]),gra[ma[i-1][j]].insert(ma[i][j]);
			}
			if(j!=0){
				if(ma[i][j-1]!=0&&ma[i][j]!=0)gra[ma[i][j]].insert(ma[i][j-1]),gra[ma[i][j-1]].insert(ma[i][j]);
			}
		}
	}
	dfs(1);//著色 
	for(int i=0;i<30;i++){//輸出 
		if(color[ma[i][0]])cout<<(char)('a'+color[ma[i][0]]-1);
		else cout<<'`';
		for(int j=1;j<30;j++){
			if(color[ma[i][j]])cout<<" "<<(char)('a'+color[ma[i][j]]-1);
			else cout<<" `";
		}cout<<endl;
	}
	return 0;
}

code

量杯問題

題目敘述

給你量杯數量\(n\)與量杯容量\(a_i\)
能做的操作有倒滿量杯、倒光量杯

、從一杯水倒到另一杯水
問特定容量\(t\)能否被量出

輸入格式

第一行有一個量杯數量 n(\(1\leq n \leq 5\))

第二行有 n個數字

分別代表每一個量杯的容量(\(1\leq a_i \leq 50\))

第三行有一個數字t(\(1\leq t \leq 50\))

代表要量出的水量

輸出格式

輸出一個數字
有解時輸出最少步驟數
無解時輸出-1

範例:

輸入:
3
5 8 11
2
輸出:
4

作法

  1. 把n個量杯的水量視為狀態,在狀態間bfs
  2. bfs時利用hash+set檢查狀態是否曾出現
  3. 當找到答案或遍歷完所有狀態後跳出

bfs

bfs

while(queue.size()){
	int front=queue.front();queue.pop();
	for(auto i:front){
		if(visited[i]==0)queue.push(i),visited[i]=1;
	}
}

rolling hash

把一個序列對應到一個數
實現快速判斷序列相同

\((\Sigma a_i*p^{n-i})\%M\)

p是個比p大的質數

M是個夠大的質數

code


int n,arr[5]={},t,ans=-1,anss,cnt=0,pos=1;//n:量杯數量 arr:量杯容量 t:要求容量 ans:答案 cnt:目前步驟數 
pair<int,vector<int>> stat[10000000];
const int RANDOM = chrono::high_resolution_clock::now().time_since_epoch().count();
struct chash {
    int operator()(int x) const { return x ^ RANDOM; }
};
gp_hash_table<int,bool> ma;//存hash值 
//map<int,bool> ma;
queue<pair<vector<int>,int>,list<pair<vector<int>,int>>> q;//bfs用 queue 
const ll MOD=1e9+7;
const ll mul=1117;
inline bool check(vector<int> a){//檢查hash值 
	ll val=0;
	for(int i=0;i<n;i++)val+=a[i],val=(val*mul)%MOD;
	if(ma[val])return 0;
	else return 1;
}
inline void add(vector<int> a){//新增hash值 
	ll val=0;
	for(int i=0;i<n;i++)val+=a[i],val=(val*mul)%MOD;
	ma[val]=1;
	return ;
}
int main(){
	clock_t st,no;
	cin>>n;
	for(int i=0;i<n;i++)cin>>arr[i];
	cin>>t;//輸入 
	st=clock();
	int g=arr[0],big=1;//g:存gcd big:紀錄有無比要求容量小的量杯 
	for(int i=0;i<n;i++){
		if(arr[i]==t){//判掉一步解方便後續程式設計 
			cout<<1<<endl;
			return 0;
		}
		if(arr[i]>t)big=0;//判掉要求容量大於每個杯子 
		g=__gcd(g,arr[i]);//判掉gcd不合的解 
	}
	if(big||(t%g)){
		cout<<-1<<endl;
		return 0;
	}
	vector<int> sta,tmp;
	sta.reserve(5);tmp.reserve(5);
	for(int i=0;i<n;i++)sta.pb(0);//初始狀態 
	stat[0].F=-1;
	stat[0].S=sta;
	q.push(mp(sta,0));
	while(q.size()){//bfs
		cnt++;
		no=clock();
		cout<<cnt<<" "<<double(no-st)/CLOCKS_PER_SEC<<"秒"<<endl;
		int siz=q.size();
		while(siz--){//同一步驟數一起跑 
			sta=q.front().F;int now=q.front().S;q.pop();
			for(int i=0;i<n;i++){
				if(sta[i]!=arr[i]){//倒滿 
					tmp=sta;tmp[i]=arr[i];
					if(check(tmp)){
						stat[pos].F=now;
						stat[pos].S=tmp;
						q.push(mp(tmp,pos)),add(tmp);pos++;
					}
				}
			}
			for(int i=0;i<n;i++){
				if(sta[i]!=0){//清空 
					tmp=sta;tmp[i]=0;
					if(check(tmp)){
						stat[pos].F=now;
						stat[pos].S=tmp;
						q.push(mp(tmp,pos)),add(tmp);pos++;
					}
				}
			}
			for(int i=0;i<n;i++){
				for(int j=0;j<n;j++){
					if(i!=j)if(sta[i]!=0&&(arr[j]-sta[j])){//i倒到j 
						tmp=sta;
						ll ttmp=min(tmp[i],arr[j]-tmp[j]);
						tmp[j]+=ttmp;
						tmp[i]-=ttmp;
						if((tmp[i]==t||tmp[j]==t)&&ans==-1)ans=cnt,anss=pos;//檢查答案 
						if(check(tmp)){
							stat[pos].F=now;
							stat[pos].S=tmp;
							q.push(mp(tmp,pos)),add(tmp);pos++;
						}
					}
				}
			}
		}
		if(ans!=-1)break;
	}
	no=clock();
	cout<<ans<<endl;//" "<<double(no-st)/CLOCKS_PER_SEC<<"秒"<<endl;
	stack<vector<int>> Qq;
	while(stat[anss].F!=-1){
		Qq.push(stat[anss].S);
		anss=stat[anss].F;
	}
	while(Qq.size()){
		for(auto i:Qq.top())cout<<i<<" ";
		cout<<endl;Qq.pop();
	}
	return 0;
}

直線

題目敘述

給你直線的兩端點

畫出直線

輸入格式

有四個數\(x_1,y_1,x_2,y_2\)

分別代表第一個點的x,y座標
跟第二的點的x,y座標

輸出格式

輸出一個字元矩陣
*代表直線經過的點
O代表直線兩端點

其餘輸出一格空白

例子

輸入:7 1 2 2

輸出:

作法

  1. 從x較小的端點開始畫
  2. x+1的同時利用直線方程式
    判斷y與y+1哪個離直線較近
  3. 到達另一端點後結束

code

char cor[101][101];//儲存圖 
int main(){
	int x,y,dx,dy;
	cin>>x>>y>>dx>>dy;
	if(x>dx)swap(x,dx),swap(y,dy);//交換順序方便後續迴圈設計 
	for(int i=1;i<=100;i++)for(int j=1;j<=100;j++)cor[i][j]=' ';//初始化 

	int tmp=y;//儲存當前y座標值 
	for(int i=x;i<=dx;i++){//以i遍歷x座標 
		if(y>dy){//y由大到小 
			while(tmp>dy&&abs((y-dy)*(i-x)-(y-tmp)*(dx-x))>=abs((y-dy)*(i-x)-(y-tmp+1)*(dx-x))){
				tmp--;cor[i][tmp]='*';	//當y-1比y距離線更近 畫點並y-- 
			}cor[i][tmp]='*';
		}else{
			while(tmp<dy&&abs((dy-y)*(i-x)-(tmp-y)*(dx-x))>=abs((dy-y)*(i-x)-(tmp+1-y)*(dx-x))){
				tmp++;cor[i][tmp]='*';//當y+1比y距離線更近 畫點並y++ 
			}cor[i][tmp]='*';
		}
	}
	cor[x][y]='O';cor[dx][dy]='O';//設定兩端點 
	for(int i=1;i<=100;i++){
		for(int j=1;j<=100;j++){
			cout<<cor[i][j];//輸出 
		}cout<<endl;
	}
	return 0;
}

橢圓

題目敘述

給你橢圓方程式

\(a^2x^2+b^2y^2+ab=0\)

的a跟b
畫出以原點為中心的橢圓

輸入格式

有兩個數\(a,b\)

分別代表方程式中的\(a,b\)

輸出格式

輸出一個字元矩陣
*代表橢圓經過的點

其餘輸出一格空白

例子

輸入:10 10

輸出:

作法

  1. 用偏微分將四分之一橢圓分為
    x變化較大與y變化較大的部分
  2. 在x變化較大時利用中點套橢圓判斷式
    的值決定y是否改變
  3. 在y變化較大時利用中點套橢圓判斷式
    ​的值決定x是否改變

code

char arr[200][200];//儲存輸出的圖 
int main(){
	int sx,sy,a,b;cin>>sx>>sy>>a>>b;
	double aa=a,bb=b;//轉為double 
	for(int i=0;i<200;i++)for(int j=0;j<200;j++)arr[i][j]=' ';//初始化 
	int x=0,y=b;double d=bb*bb+aa*aa*(bb-0.5)*(bb-0.5)-aa*aa*bb*bb;//xy座標,判別式量值 
	arr[sx][sy+b]='*';//畫圖 
	arr[sx][sy-b]='*';
	arr[sx][sy+b]='*';
	arr[sx][sy-b]='*';
	while(1){
		if(d<0){//判別式小於0 
			d+=bb*bb*(2*(double)x+3);//更新辦別式差值 
			x++;//更新座標 
		}else{
			d+=bb*bb*(2*(double)x+3)+aa*aa*(-2*(double)y+2);//更新辦別式差值 
			x++;y--;//更新座標
		}
		arr[sx+x][sy+y]='*';//畫圖 
		arr[sx-x][sy+y]='*';
		arr[sx+x][sy-y]='*';
		arr[sx-x][sy-y]='*';
		if(bb*bb*((double)x+1)>=aa*aa*((double)y-0.5))break;
	}
	d=bb*bb*((double)x+0.5)*((double)x+0.5)+aa*aa*((double)y-1)*((double)y-1)-aa*aa*bb*bb;//重設判別式 
	while(1){
		if(d<0){//判別式小於0 
			d+=bb*bb*(2*(double)x+2)+aa*aa*(-2*(double)y+1);//更新辦別式差值 
			x++;y--;//更新座標
		}else{
			d+=aa*aa*(-2*(double)y+1);//更新辦別式差值 
			y--;//更新座標
		}
		arr[sx+x][sy+y]='*';//畫圖 
		arr[sx-x][sy+y]='*';
		arr[sx+x][sy-y]='*';
		arr[sx-x][sy-y]='*';
		if(y<=0)break;
	}
	for(int i=0;i<200;i++){
		for(int j=0;j<200;j++){
			cout<<arr[i][j];//輸出 
		}cout<<endl;
	}
	return 0;
}

資訊專題

By linki1010111