0
点赞
收藏
分享

微信扫一扫

算法刷题记录(Day 38)

一叶轻舟okok 2022-04-04 阅读 37
算法

Necklace of Beads(poj 1286)

原题链接
题目类型:置换群和polya定理

思考过程:求解n的时候,可否利用n-1来进行求解?->由于题目中有关于对称性的限制,因此可能会存在一定的问题。

使用floya定理,那么如何去求解关于置换的个数的问题呢?

思路一:(自己的推导)
对于沿着对称轴形成的置换,n个点共有n条对称轴,若n为奇数,则n个对称轴所形成的置换都是分解为(n+1)/2。若n为偶数,则n/2个对称轴(对称轴不经过点)形成的置换分解为n/2,n/2个对称轴(经过点)形成的置换分解为n/2+1个。

对于旋转形成的置换,若n为偶数,则1个置换被分解为n个,n/2-1个置换(旋转的次数为偶数)被分解为2个,n/2个置换(旋转的次数为奇数)被分解为1个。

//WA
#include<iostream>
using namespace std;
#define NMAX 30
long long e3[NMAX];
long long n;
int main() {
	e3[0] = 1;
	for (int i = 1; i < NMAX; i++) e3[i] = e3[i - 1] * 3;
	while (1) {
		cin >> n;
		if (n == -1) break;
		if (n == 0) cout << 1 << endl;
		else if (n % 2) {
			cout << (n * e3[(n + 1) / 2] + e3[n] + (n - 1) * e3[1]) / (2 * n) << endl;
		}
		else cout << (n / 2 * e3[n / 2] + n / 2 * e3[n / 2 + 1] + e3[n] + (n / 2 - 1) * e3[2] + n / 2 * e3[1]) / (2 * n) << endl;

	}
}

在提交过程中,发现是错误的。

思路二:
对于沿着对称轴所形成的置换,得到的结论是正确的。
但是对于旋转形成的置换,则是错误的。
对于编号为x的点,若经过旋转i次的置换,会变为x+i。则以1为起点的循环节可以表示为1->1+i->1+2i->1+3i…->1。假设循环节的长度为k,则有ki+1同余1模上n。因此有n|ki,k最小值为n/gcd(n,i),即每一个循环节的长度都为n/gcd(n,i),因此共有gcd(n,i)个循环节。
在这里插入图片描述

思路参考链接

#include<iostream>
using namespace std;
#define NMAX 30
long long e3[NMAX];
long long n;
long long gcd(int y, int x) {
	return y % x == 0 ? x : gcd( x , y % x);
}
int main() {
	e3[0] = 1;
	for (int i = 1; i < NMAX; i++) e3[i] = e3[i - 1] * 3;
	while (1) {
		cin >> n;
		if (n == -1) break;
		if (n == 0) cout << 0 << endl;
		else if (n % 2) {
			long long res = n * e3[(n + 1) / 2];
			for (long long i = 1; i <= n; i++) {
				res += e3[gcd(i, n)];
			}
			cout << res / (2*n) << endl;
		}
		else { 
			long long res = n / 2 * e3[n / 2] + n / 2 * e3[n / 2 + 1];
			for (long long i = 1; i <= n; i++) {
				res += e3[gcd(i, n)];
			}
			cout << res / (2 * n) << endl;
		}

	}
}

参考链接:
旋转i次,对于循环节数为gcd(i,n)的证明
polya定理

Let it Bead(poj 2409)

原题链接

不同的手链,一定是经过旋转,翻转之后仍然不相同的个数,因此,本题在本质上是和上一题一样的,但是颜色色个数不再只是一个常量。

需要注意的是,pow没有参数类型都为int的函数原型,因此需要先将传入的参数首先转化为float.
在这里插入图片描述
在这里插入图片描述

#include<iostream>
#include<cmath>
using namespace std;
long long s;
long long c;
long long gcd(long long y, long long x) {
	return y % x == 0 ? x : gcd( x , y % x);
}
int main() {
	while (1) {
		cin >> c >> s;
		if (c == 0 && s == 0) break;
		if (s == 0) cout << 0 << endl;
		else if (s % 2) {
			long long res = s * (long long)pow((float)c, (float)((s + 1) / 2));
			for (long long i = 1; i <= s; i++) {
				res += (long long)pow((float)c, (float)gcd(i, s));
			}
			cout << res / (2*s) << endl;
		}
		else { 
			long long res = s / 2 * (long long)pow((float)c, (float)(s / 2)) + s / 2 * (long long)pow((float)c, (float)(s / 2 + 1));
			for (long long i = 1; i <= s; i++) {
				res += (long long)pow((float)c, (float)gcd(i, s));
			}
			cout << res / (2 * s) << endl;
		}

	}
}

Cow Sorting(poj 3270)

原题链接

思路一:
思考过程:本质上是一个置换,且最终的排列顺序是已知的,即是递增的顺序,可否使用贪心的策略,使得当前脾气最大的牛只换一次。

//WA代码
#include<iostream>
#include<map>
#include<algorithm>
using namespace std;
#define NMAX 10004
#define TMAX 100005
int N;
int temper[NMAX];
int pro[NMAX];
int lot[TMAX];
long long res = 0;
int main() {
	cin >> N;
	for (int i = 0; i < N; i++) {
		cin >> temper[i];
		pro[i] = temper[i];
		lot[temper[i]] = i;
	}
	sort(pro, pro + N);
	for (int i = N - 1; i > 0 ; i--) {
		res += (pro[i] + temper[i]);
		lot[temper[i]] = lot[pro[i]];
		swap(temper[i], temper[lot[pro[i]]]);
	}
	cout << res;
}

思路二:
参考思路
首先,这一定是一个置换,但是该置换只能通过两两相互交换得到,问题便在于如何交换使得交换的成本是最小的。将视线集中在置换环上,交换的双方都是位于同一置换环,是可以达到最终的置换效果的,即置换环内进行交换。
在一个置换环中,每一个元素都至少会被交换一次,因此为使得值最小,可以每次都使用置换环中最小的那个来进行交换,即值为X1=sum+(len-2)*min。

假如置换环中的最小元素还是很大,考虑将整个区间内最小的值Amin首先交换进入置换环中,利用Amin来进行置换环内的交换,其值有X2=sum-min+Amin+(len-2)*Amin+2(Amin+min)。

因此对于一个置换环,其值X为min(X1,X2).

那么问题便转化为,如何去求解置换环,以及置换环上的极值。
首先我们需要求解得出置换sub,sub[i]代表的是第i位需要由第sub[i]位来进行替换。逐个扫描sub数组,使用vis代表是否访问过,对于没有访问过的,使用while(j!=i)的形式来求其置换环,在遍历置换环的过程中,维护最值、和,最后输出即可。

#include<iostream>
#include<map>
#include<algorithm>
using namespace std;
#define NMAX 10004
#define TMAX 100005
int N;
int temper[NMAX];
int pro[NMAX];
int lot[TMAX];
int sub[NMAX];
int vis[NMAX];
long long res = 0;
int main() {
	cin >> N;
	for (int i = 0; i < N; i++) {
		cin >> temper[i];
		pro[i] = temper[i];
		lot[temper[i]] = i;
	}
	sort(pro, pro + N);
	int Amin = pro[0];
	memset(vis, 0, sizeof(vis));
	for (int i = 0; i < N; i++) sub[i] = lot[pro[i]];

	for (int i = 0; i < N; i++) {
		if (!vis[i]) {
			vis[i] = 1;
			int Min = 1000000, sum = temper[i], len = 1;
			int j = sub[i];
			while (j != i) {
				vis[j] = 1;
				len++;
				if (temper[j] < Min) Min = temper[j];
				sum += temper[j];
				j = sub[j];
			}
			if (len == 1) continue;//长度为1的时候不需要操作
			int X1 = sum + (len - 2) * Min;
			int X2 = sum - Min + Amin + (len - 2) * Amin + 2 * (Amin + Min);
			res += min(X1, X2);
		}
	}
	cout << res << endl;
}
举报

相关推荐

0 条评论