A.Restaurant Tables(模拟)
题意:
a张单人桌,b张双人桌,有n组人来餐厅吃饭,每组只有一个或两个人。如果是一个人来,会给他安排单人桌,如果没有单人桌,则安排双人桌,如果仍然没有双人桌,则安排在已经有一位客人的双人桌,若仍然没有,只能拒绝提供服务。如果该组有两个人,则安排双人桌,若没有双人桌,则拒绝服务。按来的顺序给出每组人数(1 or 2),问需要拒绝为多少人服务?
题解:
注意决策顺序。一个人:单人桌→双人桌→只有一个人的双人桌→拒绝服务;两个人:双人桌→拒绝服务
题目链接:http://codeforces.com/problemset/problem/828/A
代码:
#include<stdio.h>
int n,a[4],ans,num;
int main()
{
while (scanf("%d %d %d",&n,&a[1],&a[2])!=EOF)
{
ans = 0;
a[3] = 0;
for (int i=0;i<n;i++)
{
scanf("%d",&num);
if (num == 1)
{
if (a[1]) a[1]--;
else
if (a[2]) a[2]--,a[3]++;
else
if (a[3]) a[3]--;
else
ans++;
}
else
{
if (a[2]) a[2]--;
else
ans+=2;
}
}
printf("%d\n",ans);
}
return 0;
}
B.Black Square(模拟)
题意:
给出n*m矩阵包含黑色块和白色块,可以选择将白色块涂成黑色块,问最少需要涂多少个白色块,才能使所有黑色块构成一个正方形,若不能,输出-1。
题解:
找出最边缘的黑色块,将其进行左右扩展和上下扩展,成为正方形
题目链接:http://codeforces.com/problemset/problem/828/B
代码:
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
using namespace std;
char a[105][105];
int n,m,x,xx,y,yy,ans,len,k;
int main()
{
while (scanf("%d %d",&n,&m)!=EOF)
{
x = n, yy = m, y = -1, xx = -1;
ans = 0;
for (int i=0;i<n;i++)
{
scanf("%s",a[i]);
for (int j=0;j<m;j++)
if (a[i][j] == 'B')
{
x = min(i,x);
y = max(j,y);
xx = max(i,xx);
yy = min(j,yy);
}
}
//printf("%d %d %d %d\n",x,xx,y,yy);
if (xx == -1) ans=1;
else
{
len = max(y-yy+1,xx-x+1);
k = len - (xx-x+1);
while (x>0 && k>0) x--,k--;
while (xx<n-1 && k>0) xx++,k--;
if (k > 0)
{
printf("-1\n");
continue;
}
k = len - (y-yy+1);
while (yy>0 && k>0) yy--,k--;
while (y<m-1 && k>0) y++,k--;
if (k > 0)
{
printf("-1\n");
continue;
}
for (int i=x;i<=xx;i++)
for (int j=yy;j<=y;j++)
if (a[i][j] == 'W') ans++;
}
printf("%d\n",ans);
}
return 0;
}
C. String Reconstruction(模拟,排序)
题意:
每行开头输入一个字符串,并给出字符串出现的位置,问字典序最小的总串
题解:
对字符串的位置进行排序,遍历一遍,输出字符串,未知位置填’a’
题目链接:http://codeforces.com/problemset/problem/828/C
代码:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<iostream>
#include<math.h>
#include<algorithm>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#define ll long long
#define For(i,a,b) for (int i=(a);i<(b);i++)
using namespace std;
const int maxn = 1e6+10;
struct mark
{
int k,w;
}str[maxn*10];
char ans[maxn*10];
string c[maxn];
int Case,n,a,num;
bool cmp(mark x,mark y)
{
if (x.w < y.w) return 1;
else
if (x.w == y.w && c[x.k].size() > c[y.k].size())
return 1;
return 0;
}
int main()
{
scanf("%d",&Case);
For (t,0,Case)
{
cin >> c[t];
scanf("%d",&n);
For (i,0,n)
{
scanf("%d",&a);
str[num].k = t;
str[num].w = a;
num++;
}
}
sort(str,str+num,cmp);
int len = 0;
For (i,0,num)
{
int ki = str[i].k;
if (len < str[i].w)
{
len = str[i].w - 1;
For (j,0,c[ki].size())
ans[++len] = c[ki][j];
}
else
{
int start = len - str[i].w + 1;
For (j,start,c[ki].size())
ans[++len] = c[ki][j];
}
}
For (i,1,len+1)
if (ans[i] >= 'a' && ans[i]<='z')
printf("%c",ans[i]);
else
printf("a");
return 0;
}
D.High Load(贪心,构造图)
题意:
n个结点,k个结点的度为1,其余结点的度大于1,问如何连接使得距离最远的两个结点离得最近。
题解:
以一个结点为中心向k个方向尽量扩展,每个方向向外扩展的点数为(n-1)/k或(n-1)/k+1;如果所有向外扩展的点数为(n-1)/k,则距离为(n-1)/k,如果存在一个方向向外扩展的点数为(n-1)/k+1,则距离为(n-1)/k+1,如果存在大于两个方向向外扩展的点数为(n-1)/k+1,则距离为(n-1)/k+2。
题目链接:http://codeforces.com/problemset/problem/828/D
代码:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#include<algorithm>
#include<vector>
using namespace std;
const int maxn = 2*1e5+100;
vector <int> e[maxn];
int s,n,rem,k,node;
int main()
{
while (scanf("%d %d",&n,&k)!=EOF)
{
s = (n-1) / k;
rem = (n-1) % k;
if (rem == 0) printf("%d\n",2*s);
else
if (rem == 1) printf("%d\n",2*s+1);
else
printf("%d\n",2*s+2);
for (int i=1;i<=n;i++) e[i].clear();
node = 1;
while (node < n)
{
node++,e[1].push_back(node);
for (int i=1;i<s;i++)
{
e[node].push_back(node+1);
node++;
}
if (rem)
{
e[node].push_back(node+1);
node++,rem--;
}
}
for (int i=1;i<=n;i++)
for (int j=0;j<e[i].size();j++)
printf("%d %d\n",i,e[i][j]);
}
return 0;
}
G.Soldier and Number Game(贪心,数论)
题意:
一名士兵选择数n,n可表示为a!/b!,a、b都是正数,另一名士兵每次可以选择一个可以被n整除的数x,让n=n/x,最终使得n=1,选择的数字数量就是士兵的得分,问最大得分为多少。
题解:
题目要求要除以尽可能多的数,则除数必然为质数。将a!/b!中的所有数字进行质因数分解,各个质因数的指数相加即为答案。若每个数都进行分解会超时,我们可以用欧拉筛法预处理出 5*1e6 以内的质数 prime[number] 以及 对i进行质因数分解后得到的各个质因数指数和 sum[i]
sum[i] = sum[i/prime[j]] + 1;
prime[j] 为 i 可以整除的最小质因数
题目链接:http://codeforces.com/problemset/problem/546/D
代码
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#include<algorithm>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#define For(i,a,b) for (int i=(a);i<(b);i++)
using namespace std;
const int maxn = 5*1e6+10;
int t,a,b,number,prime[maxn],p[maxn],ans[maxn];
bool isprime[maxn];
void getprime()
{
For (i,2,maxn) isprime[i] = 1;
For (i,2,maxn)
{
if (isprime[i])
prime[number++] = i;
For (j,0,number)
{
if (i*prime[j] > maxn) break;
isprime[i*prime[j]] = 0;
if (i % prime[j] == 0) break;
}
}
return;
}
void solve()
{
ans[1] = 0;
For (i,2,maxn)
{
if (isprime[i]) p[i] = 1;
else
{
For (j,0,number)
if (i % prime[j] == 0)
{
p[i] = p[i/prime[j]] + 1;
break;
}
}
ans[i] = ans[i-1] + p[i];
}
return;
}
int main()
{
getprime();
solve();
scanf("%d",&t);
while (t--)
{
scanf("%d %d",&a,&b);
printf("%d\n",ans[a] - ans[b]);
}
return 0;
}
H. Kyoya and Colored Balls(排列组合)
题意:
n种颜色的球装在同一个袋子里(相同颜色的球不可分辨),每次从中抽出一个球,直到袋子里没有球。在抽球时会出现一种情况,在颜色为i+1的球被全部抽走前,已经抽走了所有颜色为i的球,问该事件发生的情况有多少种?
题解:
设所有球的个数为M,从颜色编号最大的球开始进行排列组合,则第M个被抽出的球必然为k,剩余的编号为k的球在前M-1个位置选择a[k]-1个位置;颜色为k-1的球选择一颗排在最后的空位,剩余a[k-1]-1颗球在剩余位置进行选择,以此类推……各种颜色的球排列的可能性相乘即为答案。预处理出C(i,j)
题目链接:http://codeforces.com/problemset/problem/553/A
代码:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#include<algorithm>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
using namespace std;
const int mod = 1e9 + 7;
long long f[1005][1005];
int k,tot,num[1005];
void Cij()
{
f[0][0] = 1;
for (int i=1;i<=1000;i++)
{
f[i][0] = f[i][i] = 1;
for (int j=1;j<i;j++)
f[i][j] = (f[i-1][j-1] + f[i-1][j]) % mod;
}
return;
}
int main()
{
Cij();
scanf("%d",&k);
for (int i=1;i<=k;i++)
{
scanf("%d",&num[i]);
tot += num[i];
}
long long ans = 1;
for (int i=k;i>=1;i--)
{
if (num[i] == 0) continue;
ans = (ans * f[tot-1][num[i]-1]) % mod;
tot -= num[i];
}
printf("%I64d\n",ans);
return 0;
}