SRM475 - SRM479(1-250pt,500pt)

时间:2020-12-19 13:27:13

SRM 475

DIV1 300pt

题意:玩游戏。给一个棋盘,它有1×n(1行n列,每列标号分别为0,1,2..n-1)的格子,每个格子里面可以放一个棋子,并且给定一个只含三个字母WBR,长度为n的字符串,代表每个格子的颜色。在游戏开始时,r个棋子随机摆放在这n个格子里(每个棋子摆在每个格子里的概率相同),问游戏结束时,这些格子里所剩棋子数的期望。游戏的规则为(记这个棋盘的列数为size):

   1、如果棋子在0格,则向右移动一格;

   2、如果棋子在size-1或size-2格,则向左移动一格;

   3、其他棋子若在颜色为W的格子则左移一格,若在颜色为B的格子则右移一格,若在颜色为R的格子:若该旗子目前并未移动过,则左移一格,否则返回上一步所在的格子。

   4、所有棋子移动完成后,对与所有格子,如果某个格子含有不止一个棋子,则将该格所有棋子移出棋盘。

   5、第4步完成后,棋盘的列数减少1,(即从1*size大小的棋盘变为1*(size-1)),若棋盘列数等于2,则游戏结束,否则循环进行1-5步。

   2 <= n <= 17

解法:由于棋盘大小最多也就1*17格,可以直接暴力模拟所有情况,最慢也就O(C(17,8) * 17)的复杂度,能接受。

   但是,题解给出了一种很好的方法。

   首先,通过观察发现以下几点:1、称“游戏开始时出现在奇数格的棋子“为奇数棋,其他称为偶数棋。经过一次1-3步循环,所有奇数棋变为偶数棋或者被移出棋盘,所有偶数棋变为奇数棋或被移出棋盘。

   2、每次将某一格的棋子移出棋盘,移出的数量必定为2,且移出的全为奇数棋或者全为偶数棋。换句话说,如果游戏开始时,奇数棋的数量为奇数(偶数),游戏结束时,奇数棋数量也为奇数(偶数)。偶数棋也如此。

   3、游戏结束时,棋盘大小一定为1*2,并且每格含有0个或1个棋子。也就是说,游戏结束时剩下0个或1个奇数棋,0个或1个偶数棋。

    由1,2,3可以得出结论,若游戏开始时有x个奇数棋,有y个偶数棋,则游戏结束时有(x%2 + y%2)个棋子。然后,遍历所有游戏开始时可能出现的棋子摆放情况即可。

tag:think, good

 /*
* Author: plum rain
* score : 0
* study form: www.topcoder.com
*/
#line 11 "RabbitStepping.cpp"
#include <sstream>
#include <stdexcept>
#include <functional>
#include <iomanip>
#include <numeric>
#include <fstream>
#include <cctype>
#include <iostream>
#include <cstdio>
#include <vector> using namespace std; #define SZ(v) ((int)(v).size()) class RabbitStepping
{
public:
double getExpected(string field, int r){
int n = SZ (field);
int num = , sum = ;
for (int i = ; i < (<<n); ++ i){
if (__builtin_popcount(i) != r) continue; int e = , o = ;
for (int j = ; j < n; ++ j)
if (i & (<<j)){
e += j & ;
o += - (j&);
}
num += (e%) + (o%);
++ sum;
}
return (num + 0.0) / sum;
}
};

DIV1 600pt

题意:第一年7月天上掉下一对小兔子。之后每年3月,老兔子生一对小兔子,原来的小兔子升级为老兔子;某些年的11月,消失一半兔子,消失的兔子总是年龄较大的那些。现在给定最多50个兔子会消失一半的年份,问第K(K<=1e7)年的12月一共有多少兔子。答案模MOD=1,000,000,009。

解法:不会- -

tag:math, good

SRM 476

DIV1 250pt

题意:有n只羊,给定两个数组{an}和{bn},ai表示第i只羊的食量,bi表示第i只羊的附加食量,并且给定拥有的食物总量为tot。如果主人饲养了m只羊,则对于一只羊k,它消耗的食物总量为a[k] + m*b[k]。要求所有羊消耗的食物总量之和不超过tot的情况下,求饲养的羊的数量最多为多少只。

   (1<= n <= 50)

解法:主人养k只羊时花费食物总量最少为t,则可以t可以在O(nlogn)(使用一次快排即可)。然后从k = n to 1找最多可能饲养的羊的数量。

tag:greedy

 /*
* Author: plum rain
* score : 204.20
*/
#line 11 "Badgers.cpp"
#include <sstream>
#include <stdexcept>
#include <functional>
#include <iomanip>
#include <numeric>
#include <fstream>
#include <cctype>
#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm> using namespace std; #define PB push_back
#define SZ(v) ((int)(v).size())
#define out(x) cout<<#x<<":"<<(x)<<endl
#define tst(a) cout<<#a<<endl typedef vector<int> VI; class Badgers
{
public:
int feedMost(vector <int> hun, vector <int> g, int tot){
int size = SZ (hun);
VI tmp;
for (int n = size; n > ; -- n){
tmp.clear();
for (int i = ; i < size; ++ i)
tmp.PB (hun[i] + g[i]*(n-));
sort (tmp.begin(), tmp.end()); int num = ;
for (int i = ; i < n; ++ i)
num += tmp[i];
if (num <= tot) return n;
}
return ;
}
};

DIV1 550pt

题意: 小明有一些朋友(n个),小明的朋友还有一些朋友,他们可能是小明的朋友也可能不是。一共有m个人,他们的房子标号为1-m。小明要从自己家(1号房子)出发,然后去拜访自己的朋友。每次,小明所在的房子的主人都会对房子主人自己的朋友进行一次随机排列,小明只能从前k个人中选择一个人拜访,或者不再拜访任何人。注意,选择的这个人必须是小明没有拜访过的,并且他是小明的朋友。问,在小明采取最优策略下,他能拜访完所有朋友的概率最大是多少。

   每个人最多15个朋友,一共最多36个人,K >= 1。

解法:首先,要用状态压缩,用opt表示小明的所有朋友(最多15个)是否被拜访过,1为未拜访。数组dp[i][j]表示拜访状态为i,现在在房子j的状态下能拜访完所有朋友的概率。

   dp[i][j] = sum{(给出随机排列后,小明会选择t作为下一个拜访地点的可能性)  * dp[i^(1<<t)][t]}(t为所有小明的朋友, 且i & (1<<t)为真)。

   上面状态转移方程只剩下一个问题没有解决,就是“给出随机排列后,小明会选择t作为下一个拜访地点的可能性”,而这个可以通过讨论dp[i^(1<<t)][t]的大小易求,具体见代码。

tag:状压dp, good

 /*
* Author: plum rain
* score : 0
*/
#line 11 "FriendTour.cpp"
#include <sstream>
#include <stdexcept>
#include <functional>
#include <iomanip>
#include <numeric>
#include <fstream>
#include <cctype>
#include <iostream>
#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
#include <utility> using namespace std; #define CLR(x) memset(x, 0, sizeof(x))
#define PB push_back
#define SZ(v) ((int)(v).size())
#define out(x) cout<<#x<<":"<<(x)<<endl
#define tst(a) cout<<#a<<endl typedef vector<int> VI; int C[][], num[];
double dp[<<][];
bool pat[][];
VI m;
int n, k; void C_table(int maxx)
{
CLR (C);
C[][] = ;
for (int i = ; i <= maxx; ++ i){
C[i][] = C[i][i] = ;
for (int j = ; j < i; ++ j)
C[i][j] = C[i-][j] + C[i-][j-];
}
} bool cmp(pair<double, int> a, pair<double, int> b)
{
return a.first > b.first;
} double choose(int num, int pos)
{
if (num < k)
return (double)(pos == );
if (num - pos < k) return 0.0;
return (double)C[num-pos-][k-] / C[num][k];
} double gao (int opt, int pos)
{
vector<pair<double, int> > cur; cur.clear();
for (int i = ; i < n; ++ i)
if (opt&(<<i) && (pos == - || pat[m[pos]][m[i]]))
cur.PB(make_pair(dp[opt^(<<i)][i], i)); sort (cur.begin(), cur.end(), cmp);
double ret = 0.0;
for (int i = ; i < SZ (cur); ++ i)
ret += choose(pos==-?n:num[m[pos]], i) * dp[opt^(<<cur[i].second)][cur[i].second];
return ret;
} class FriendTour
{
public:
double tourProbability(vector <string> fri, int K){
C_table(); CLR (pat); CLR (num); m.clear();
int tmp = ;
for (int i = ; i < SZ (fri); ++ i){
string s = fri[i]; tmp = ;
for (int j = ; j < SZ (s); ++ j){
if (j == SZ(s)- || s[j] == ' '){
if (j == SZ(s)-)
tmp = tmp * + s[j] -'';
-- tmp;
if (!i) m.PB (tmp);
pat[i][tmp] = ;
++ num[i];
tmp = ;
continue;
}
tmp = tmp * + s[j] - '';
}
}
n = SZ (m); k = K; CLR (dp);
for (int i = ; i < n; ++ i) dp[][i] = 1.0;
for (int i = ; i < (<<n); ++ i)
for (int j = ; j < n; ++ j)
dp[i][j] = gao (i, j);
return gao((<<n)-, -);
}
};

SRM 477

DIV1 250pt

题意:摆放六边形形成如下图案,然后将所有六边形涂成红蓝两色,求两种颜色的的六边形的公共边的总长度。

SRM475 - SRM479(1-250pt,500pt)

解法:直接分类讨论就好。题解的代码比我的代码简洁多了- -

tag:water

 /*
* Author: plum rain
* score : 176.61
*/
#line 11 "Islands.cpp"
#include <sstream>
#include <stdexcept>
#include <functional>
#include <iomanip>
#include <numeric>
#include <fstream>
#include <cctype>
#include <iostream>
#include <cstdio>
#include <vector> using namespace std; #define SZ(v) ((int)(v).size())
#define out(x) cout<<#x<<":"<<(x)<<endl
#define tst(a) cout<<#a<<endl typedef vector<string> VS;
VS k; int count (int x, int y)
{
int ret = ;
if (k[x][y+] == '.') ++ ret;
if (k[x][y-] == '.') ++ ret; if (x & ){
if (x > ){
if (k[x-][y] == '.') ++ ret;
if (y+ < SZ(k[x-]) && k[x-][y+] == '.') ++ ret;
}
if (x < SZ(k)-){
if (k[x+][y] == '.') ++ ret;
if (y+ < SZ(k[x+]) && k[x+][y+] == '.') ++ ret;
}
return ret;
} if (x > && k[x-][y] == '.') ++ ret;
if (x > && y > && k[x-][y-] == '.') ++ ret;
if (x+ < SZ(k) && k[x+][y] == '.') ++ ret;
if (x+ < SZ(k) && y > && k[x+][y-] == '.') ++ ret;
return ret;
} class Islands
{
public:
int beachLength(vector <string> kin){
k.clear();
k = kin;
int n = SZ (k);
int ans = ;
for (int i = ; i < n; ++ i){
string s = k[i];
int len = SZ (s);
for (int j = ; j < len; ++ j)
if (s[j] == '#') ans += count (i, j);
}
return ans;
}
};

DIV1 500pt

题意:如果数组(a, b)满足以下条件:1、存在a*a + b*b = c*c且a,b,c均为整数;2、a,b互素。则称a,b为好数组。给定n个数(可能重复),求从中最多可以拿出多少对好数组。

解法:有两种方法,可以将每个数拆成两个点然后求一个二分图最大匹配,ans = 匹配数/2;另一种方法,由题目条件容易推得,a,b必为一奇一偶。所以做一个奇偶二分图匹配即可。可是我不会二分图匹配- -,所以不会做.....

tag:二分图最大匹配

SRM 479

DIV1 250pt

题意:有一排座位1-n,每个座位上的客人需要咖啡或者茶,服务员每次在第0号位置将水壶添满茶或者咖啡,需要47秒。服务员的水壶装满一次只能给7个需求相同(茶或者咖啡)人倒,服务员从每个座位k走到k-1或者k+1需要1秒,给每个人倒水需要4秒。给出每个客人的需求,问服务员满足所有客人的需求所需要的最短时间。

解法:贪心即可。之前自己写的代码好搓,还错了- -,然后看了summary里面别人的代码,自己又写了一份。

tag:greedy