51nod 1449 砝码问题

​http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1449​

现在有好多种砝码,他们的重量是 w0,w1,w2,… 每种各一个。问用这些砝码能不能表示一个重量为m的东西。
样例解释:可以将重物和3放到一个托盘中,9和1放到另外一个托盘中。

Input
单组测试数据。
第一行有两个整数w,m (2 ≤ w ≤ 10^9, 1 ≤ m ≤ 10^9)。
Output
如果能,输出YES,否则输出NO。
Input示例
3 7
Output示例
YES

分析:贪心,直接模拟即可。

#include <iostream>
#include <cstdio>
#include <map>
using namespace std;

typedef long long LL;
LL myabs(LL key){
return key>0?key:-key;
}
bool check(LL w,LL m){
LL val = m;
map<int,int> mp;
while(val != 0){
LL var = 1;
LL res1 = val;
LL res2 = val;
LL tag = 0;
while(res1 >= res2){
res1 = res2;
res2 = myabs(val-var);
if(res1 == res2) break;
tag = var;
var = var*w;
}
if(val>res1 && mp[tag]==0){
val = res1;
mp[tag]=1;
}
else break;
}
return val;
}
int main()
{
int w,m;
while(~scanf("%d%d",&w,&m)){
if(check(w,m) ==0 )puts("YES");
else puts("NO");
}
return 0;
}

51nod 1629 B君的圆锥

​http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1629​​​
B君要用一个表面积为S的圆锥将白山云包起来。
B君希望包住的白山云体积尽量大,B君想知道体积最大可以是多少。
注意圆锥的表面积包括底面和侧面。

分析: PI∗r∗l+PI∗r2=S
V=PI∗r2∗h3
由以上两式得到由r表示的V,然后对r三分查找最大的V。

/*
============================================================================
Name : 1629.c
Author : theArcticOcean
Version :
Copyright :
Description : C, Ansi-style
============================================================================
*/

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

double error = 1e-8;
double PI = acos(-1);
double calc(double r,double S){
double part1 = PI*r*r/3.0;
double part2 = sqrt(S*S/PI/PI/r/r-2*S/PI);
return part1*part2;
}
int main(void) {
double S;
while(~scanf("%lf",&S)){
double low=0,high=sqrt(S/PI),mid1,mid2,v1,v2;
while(high-low > error){
mid1 = low+(high-low)/3;
mid2 = high-(high-low)/3;
v1 = calc(mid1,S);
v2 = calc(mid2,S);
if(v1-v2 > error) high = mid2;
else low = mid1;
}
printf("%lf\n",calc(mid1,S));
}
return

51nod 1122 机器人走方格 V4

​http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1122​

四个机器人a b c d,在2 * 2的方格里,一开始四个机器人分别站在4个格子上,每一步机器人可以往临近的一个格子移动或留在原地(同一个格子可以有多个机器人停留),经过n步后有多少种不同的走法,使得每个毯子上都有1机器人停留。由于方法数量巨大,输出 Mod 10^9 + 7的结果。
Input
输入1个数N(0 <= N <= 10^9)
Output
输出走法的数量 Mod 10^9 + 7
Input示例
1
Output示例
9

分析:“经过多少步有多少种走法”,这让人联想到图论、矩阵连乘。由“每一步可以往临近的一个格子移动或留在原地”,我们想象出

algorithm 题集七 (17.01.30)_dp


能画出这样的矩阵(第i行代表第i个机器人,第j列代表第j个格子):

⎛⎝⎜⎜⎜1110110110110111⎞⎠⎟⎟⎟


因为每一个格子都必须有机器人,所以四个机器人在方格的位置情况集合一定是{1,2,3,4}的全排列。每一种情况是每个机器人到达最终位置的方法数的乘积。


将原矩阵进行N次幂的计算得到矩阵ans,那么ans.m[i][j]就是第i个机器人到第j个格子的方案数。所以,快速幂和next_permutation是我们需要的。

#include <iostream>
#include <stdio.h>
#include <algorithm>
using namespace std;

typedef long long LL;
LL mod = 1e9 + 7;
LL I[4][4]={
1,0,0,0,
0,1,0,0,
0,0,1,0,
0,0,0,1
};
LL A[4][4]={
1,1,1,0,
1,1,0,1,
1,0,1,1,
0,1,1,1
};
class Matrix{
public:
LL m[4][4];
Matrix(LL k[4][4]){
int i,j;
for(i=0;i<4;i++){
for(j=0;j<4;j++) m[i][j] = k[i][j];
}
}
Matrix operator*(const Matrix other){
int i,j,k;
Matrix ans(I);
for(i=0;i<4;i++){
for(j=0;j<4;j++){
ans.m[i][j] = 0;
for(k=0;k<4;k++){
ans.m[i][j] += this->m[i][k]*other.m[k][j];
}
ans.m[i][j] = ans.m[i][j]%mod;
}
}
return ans;
}
Matrix operator=(const Matrix other){
int i,j;
for(i=0;i<4;i++){
for(j=0;j<4;j++){
this->m[i][j] = other.m[i][j];
}
}
return *this;
}
Matrix operator^(const int n){
Matrix ans(I);
Matrix temp = *this;
int t = n;
while(t){
if(t&1) ans=ans*temp;
temp=temp*temp;
t>>=1;
}
return ans;
}
};
int main()
{
int N;
while(scanf("%d",&N) != EOF){
Matrix Ma(A);
Matrix ans = Ma^N;
//cout<<"yes"<<endl;
int i,j;
LL result = 0;
int arr[4]={0,1,2,3};
do{
LL temp = 1;
for(i=0;i<4;i++){
temp = temp*ans.m[i][arr[i]]%mod;
//while(temp>=mod) temp-=mod;
}
result=result+temp;
while(result>=mod) result -= mod;
}while(next_permutation(arr,arr+4));
printf("%lld\n",result);
}
return 0;
}

51nod 1092 回文字符串

​http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1092​​​
回文串是指aba、abba、cccbccc、aaaa这种左右对称的字符串。每个字符串都可以通过向中间添加一些字符,使之变为回文字符串。
例如:abbc 添加2个字符可以变为 acbbca,也可以添加3个变为 abbcbba。方案1只需要添加2个字符,是所有方案中添加字符数量最少的。
分析:多余的字符也就是需要增添的字符,把字符串逆序和源字符串进行对比可以得到答案。这里我们需要求解最长公共子序列(不要求连续)

/*
============================================================================
Name : 1092.c
Author : theArcticOcean
Version :
Copyright :
Description : C, Ansi-style
============================================================================
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char str[1005], str2[1005];
int C[1005][1005];
int main(void) {
int length;
int i,j;
while(~scanf("%s",str+1)){
length = strlen(str+1);
for(i=1;i<=length;i++){
str2[length+1-i] = str[i];
}
str2[length+1] = 0;
memset(C,0,sizeof(C));
for(i=1;i<=length;i++){
for(j=1;j<=length;j++){
if(str[i] == str2[j]){
C[i][j] = C[i-1][j-1]+1;
}
else if(C[i][j-1] > C[i-1][j]){
C[i][j] = C[i][j-1];
}
else {
C[i][j] = C[i-1][j];
}
}
}
int ans = length-C[length][length];
printf("%d\n",ans);
}
return

nyist 104 最大和

​http://acm.nyist.net/JudgeOnline/problem.php?pid=104​​​
描述

给定一个由整数组成二维矩阵(r*c),现在需要找出它的一个子矩阵,使得这个子矩阵内的所有元素之和最大,并把这个子矩阵称为最大子矩阵。 
例子:
0 -2 -7 0
9 2 -6 2
-4 1 -4 1
-1 8 0 -2
其最大子矩阵为:

9 2
-4 1
-1 8
其元素总和为15。

分析:求解最大子矩阵的问题,联想最大子序列的问题​,这里我们受到启发,解决问题的思路引入二维中。
假设矩阵是m行n列:

for(i=1;i<=m;i++){
for(j=i;j<=m;j++){
//把列处理(看成)一维的数字, 然后处理最大子序列

时间:O(m^2*n)
code:

#include <iostream>
#include <cstdio>
using namespace std;
typedef long long LL;
LL c[105]; //统计每列的数字和
LL g[105][105];
int main()
{
int t;
int m,n;
cin>>t;
while(t--){
scanf("%d%d",&m,&n);
int i,j,k;
for(i=1;i<=m;i++){
for(j=1;j<=n;j++){
scanf("%lld",&g[i][j]);
}
}
LL ans = g[1][1];
for(i=1;i<=m;i++){
for(j=i;j<=m;j++){
for(k=1;k<=n;k++){
if(i==j) c[k]=g[i][k];
else c[k]=c[k]+g[j][k];
}
LL sum = c[1],Max = sum;
for(k=2;k<=n;k++){
if(sum+c[k] > c[k]) sum = c[k]+sum;
else sum = c[k];
Max=Max>sum?Max:sum;
}
ans=ans>Max?ans:Max;
}
}
printf("%lld\n",ans);
}
return 0;
}

木桶排序

木桶排序的大致过程:
源数组是old[n]
新开一个数组new[0,1,——,n]
i: 0–n-1, 让new[i]为空
i: 0–n-1, 将old[i]放进new[j]中,其中j=old[i]/interval
对每一个new[i](选择)排序。
最后将new[i]连起来。

简单版:

/*
============================================================================
Name : bucket_sort.c
Author : theArcticOcean
Version :
Copyright :
Description : C, Ansi-style
============================================================================
*/

#include <stdio.h>
#include <stdlib.h>

int new[105][105];
int new_top[105];
int new_arr[105],top;
int old[105]={1,4,2,6,5,13,29,7,14,40,35,23,87,3};

/*
int compare(const void *p1,const void *p2){
return *((int *)p1) > *((int *)p2);
}
*/
void bucket_sort(int length){
int i,j,k;
int n = length;
int interval = 10;
for(i=0;i<n;i++){
int dex = old[i]/interval;
new[dex][new_top[dex]++] = old[i];
}
for(i=0;i<n;i++){
for(j=1;j<new_top[i];j++){
if(new[i][j] < new[i][j-1]){
int temp = new[i][j];
new[i][j] = new[i][j-1];
for(k=j-2;k>=0 && new[i][k]>temp;k--){
new[i][k+1] = new[i][k];
}
new[i][k+1] = temp;
}
}
//qsort(new[i],new_top[i],sizeof(int),compare);
}
for(i=0;i<n;i++){
for(j=0;j<new_top[i];j++){
new_arr[top++] = new[i][j];
}
}
}
int main(void) {
bucket_sort(14);
int i;
for(i=0;i<top;i++){
printf("%-3d",new_arr[i]);
}
return

接下来是一个稍复杂的情况:
随机产生100万范围内的整数值,然后分到m个木桶中(m<=16),用quick_sort进行桶内排序。最后输出结果和所花时间。
我们将木桶排序的结果输出到bucket.txt中,将快速排序输出到bucket2.txt中,然后对比。

/*
============================================================================
Name : bucket.c
Author : theArcticOcean
Version :
Copyright :
Description : C, Ansi-style
============================================================================
*/

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define N 1000000
#define MAX_VALUE 1024
/* bucket number is 16 , interval is 2^16. 1000000/2^16=15 */
#define BUCKET_NUM 16

int partion(int *a,int low,int high){
int i=low,j=high;
int temp=a[low];
while(i<j){
while(i<j && a[j]>=temp) j--;
a[i]=a[j]; // i are more
while(i<j && a[i]<=temp) i++;
a[j]=a[i]; // j are more
}
a[i]=temp; // at high , i=j
return i;
}
void *QuickSort(int *arr,int low,int high){
if(low < high){
int d = partion(arr,low,high);
QuickSort(arr,low,d);
QuickSort(arr,d+1,high);
}
return NULL;
}
int myrandom(int x){
return (int)((double)rand()/RAND_MAX*x+0.5);
}
/* just for check */
int compare(const void *p1,const void *p2){
return *((int *)p1) - *((int *)p2);
}

int unsorted[N];
int sorted[N];
int bucket_top[BUCKET_NUM];
int main(void) {
freopen("bucket.txt","w",stdout);
srandom(time(0));
int i;
for(i=0;i<N;i++){
unsorted[i] = myrandom(N);
}
int **buckets = (int **)malloc(sizeof(int *)*BUCKET_NUM);
int interval = 1<<BUCKET_NUM;
for(i=0;i<BUCKET_NUM;i++){
buckets[i] = (int *)malloc(sizeof(int)*interval);
}
clock_t start, end;
start = clock();
for(i=0;i<N;i++){
int dex = unsorted[i]/interval;
buckets[dex][bucket_top[dex]++] = unsorted[i];
}
int sorted_top = 0;
for(i=0;i<BUCKET_NUM;i++){
QuickSort(buckets[i],0,bucket_top[i]-1);
int j;
for(j=0;j<bucket_top[i];j++){
sorted[sorted_top++] = buckets[i][j];
}
}
end = clock();
printf("时间:%.8lf\n",(double)(end-start)/CLOCKS_PER_SEC);
for(i=0;i<BUCKET_NUM;i++) {
if(buckets[i]){
free(buckets[i]);
buckets[i] = NULL;
}
}
if(buckets){
free(buckets);
buckets = NULL;
}
printf("after sorted : \n");
for(i=0;i<N;i++){
printf("%d\n",sorted[i]);
}
/* just for check */
freopen("bucket2.txt","w",stdout);
start = clock();
qsort(unsorted,N,sizeof(int),compare);
end = clock();
printf("时间:%.8lf\n",(double)(end-start)/CLOCKS_PER_SEC);
printf("after sorted : \n");
for(i=0;i<N;i++){
printf("%d\n",unsorted[i]);
}
return

执行:

[edemon@CentOS src]$ gcc -o bucket bucket.c
[edemon@CentOS src]$ ./bucket
[edemon@CentOS src]$ ls
bucket bucket2.txt bucket.c bucket.txt
[edemon@CentOS src]$ diff bucket.txt bucket2.txt
1c1
< 时间:0.29000000
---

nyist 17 单调递增最长子序列

​http://acm.nyist.net/JudgeOnline/problem.php?pid=17&rec=rec​​​
描述

求一个字符串的最长递增子序列的长度
如:dabdbf最长递增子序列就是abdf,长度为4
输入
第一行一个整数0<n<20,表示有n个字符串要处理
随后的n行,每行有一个字符串,该字符串的长度不会超过10000
输出
输出字符串的最长递增子序列的长度

分析:对于每一个出现的字母,寻找与之匹配的最长前缀,最终的答案就是串最长前缀的长度。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(){
int n;
char str[10007];
short map[30];
scanf("%d",&n);
while(n--){
scanf("%s",str);
memset(map,0,sizeof(map));
map[str[0]-'a']++;
int len = strlen(str);
int i, j, ans;
ans = map[str[0]-'a'];
for(i=1;i<len;i++){
int dex = str[i]-'a';
int maxv = 0;
for(j=0;j<dex;j++){
maxv = maxv>map[j]?maxv:map[j];
}
map[dex] = maxv+1;
ans = ans>map[dex]?ans:map[dex];
}
printf("%d\n",ans);
}
return 0;
}

nyist 20 吝啬的国度

​http://acm.nyist.net/JudgeOnline/problem.php?pid=20​​​
描述:
在一个吝啬的国度里有N个城市,这N个城市间只有N-1条路把这个N个城市连接起来。现在,Tom在第S号城市,他有张该国地图,他想知道如果自己要去参观第T号城市,必须经过的前一个城市是几号城市(假设你不走重复的路)。

关键词:递归,回溯,搜索。

#include <iostream>
#include <stdio.h>
#include <vector>
using namespace std;

const int N = 100003;
vector<int> vec[N];
int ans[N];
bool tag[N];
void search(int key){
int len = vec[key].size(), i;
tag[key] = 1;
for(i=0;i<len;i++){
int to = vec[key][i];
if(tag[to] == 0){
search(to);
ans[to] = key;
}
}
}
int main(){
int t,n,s;
cin>>t;
while(t--){
scanf("%d%d",&n,&s);
int i,a,b;
for(i=1;i<=n;i++){
vec[i].clear();
tag[i] = 0;
}
for(i=1;i<n;i++){
scanf("%d%d",&a,&b);
vec[a].push_back(b);
vec[b].push_back(a);
}
search(s);
ans[s] = -1;
for(i=1;i<n;i++){
printf("%d ",ans[i]);
}
printf("%d\n",ans[n]);
}
return 0;
}

nyist 491 幸运三角形

​http://acm.nyist.net/JudgeOnline/problem.php?pid=491​

描述

话说有这么一个图形,只有两种符号组成(‘+’或者‘-’),图形的最上层有n个符号,往下个数依次减一,形成倒置的金字塔形状,除第一层外(第一层为所有可能情况),每层形状都由上层决定,相邻的符号相同,则下层的符号为‘+’,反之,为‘-’;如下图所示(n = 3 时的两种情况):

algorithm 题集七 (17.01.30)_sort_02


如果图中的两种符号个数相同,那这个三角形就是幸运三角形,如上图中的图(2).

分析:20个数字不多。直接计算,然后打表。

异或计算:

#include <stdio.h>
#include <stdlib.h>

int main(){
int a = 20,i,j;
int ans = 0;
int len = (1<<a)-1;
for(i=0;i<=len;i++){
int length = a-1;
int val = i;
int ones = 0;
for(j=0;j<a;j++){
if((1<<j)&i) ones++;
}
while(length>0){
int val1 = val>>1;
int temp = val1^val;
for(j=0;j<length;j++){
if((1<<j)&temp) ones++;
}
length--;
val = temp;
}
if(ones == 105) ans++;
}
printf("%d\n",ans);
return 0;
}

存储结果:

#include <stdio.h>
#include <stdlib.h>

int main(){
int a[21] = {0,0,0,4,6,0,0,12,40,0,0,171,410,0,0,1896,5160,0,0,32757,59984};
int n;
while(~scanf("%d",&n)){
printf("%d\n",a[n]);
}
return 0;
}

好吧,原来不需要计算20的情况。我想多了。

nyist 128 前缀式计算

​http://acm.nyist.net/JudgeOnline/problem.php?pid=128​​​
有时,原网站不能用,可以进入:
​​​http://115.159.40.116/problem_show.php?pid=4740​​​
描述

先说明一下什么是中缀式:

如2+(3+4)*5这种我们最常见的式子就是中缀式。

而把中缀式按运算顺序加上括号就是:(2+((3+4)*5))

然后把运算符写到括号前面就是+(2 *( +(3 4) 5) )

把括号去掉就是:+ 2 * + 3 4 5

最后这个式子就是该表达式的前缀表示。

给你一个前缀表达式,请你计算出该前缀式的值。

比如:

+ 2 * + 3 4 5的值就是 37

分析:我们知道后缀表达式(逆波兰)可以通过简单的栈操作来实现,参见求解逆波兰表达式的值​,本题是前缀情况(波兰),其实也能借助同样的思想来解决的。我们从左到右一次读取每一个元素,符号保存到一个符号栈中,数字保存浮点数栈中,当连续存储两个浮点数时立刻弹出运算符和两个栈顶浮点数进行运算,然后新值存储在浮点栈中,继续这样的操作,直到得到最后一个数字,那就是答案。
为了便于发现“连续的两个数字”,用一个单独的栈,专门标记数字和运算符并维护它。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// when we meet two numbers, we output answer.
// '+'-->43 '-'-->45 '*'-->42 '/'-->47
#define error 1e-8
double number[1050];
char stack[1050],buff[150];
char flag[1050]; // record number or character in 1 or -1.
int main()
{
int sp = 0, np = 0, fp = 0;
while(~scanf("%s",buff)){
if(buff[0] < 48 && buff[0]) {
stack[sp++] = buff[0];
flag[fp++] = -1;
}
else if(buff[0]){
number[np++] = atof(buff);
flag[fp++] = 1;
while(fp>=2 && flag[fp-1]==1 && flag[fp-2]==1){
double val2 = number[--np];
double val1 = number[--np];
char op = stack[--sp];
fp -= 3;
double temp = 0;
if(op == '+') temp = val1+val2;
else if(op == '-') temp = val1-val2;
else if(op == '*') temp = val1*val2;
else if(op == '/') temp = val1/val2;
number[np++] = temp;
flag[fp++] = 1;
}
if(np == 1 && sp==0){
printf("%.2lf\n",number[--np]);
np = sp = fp = 0;
}
}
memset(buff,0,sizeof(buff));
}
return 0;
}

我在讨论区中发现了一份逆天的代码:

#include<stdio.h>
#include<stdlib.h>
double z()
{
char a[10];
if(!~scanf("%s", a))
exit(0);
switch(*a)
{
case'+':return z() + z();
case'-':return z() - z();
case'*':return z() * z();
case'/':return z() / z();
default:return atof(a);
}
}
main()
{
while(1)
printf("%.2f\n", z());
}

直接递归下去,不得不说眼光非常犀利。向作者致敬:ares、7楼的coder(本尊)

nyist 91 阶乘之和

​http://acm.nyist.net/JudgeOnline/problem.php?pid=91​​​
描述

给你一个非负数整数n,判断n是不是一些数(这些数不允许重复使用,且为正数)的阶乘之和,如9=1!+2!+3!,如果是,则输出Yes,否则输出No;
输入
第一行有一个整数0<m<100,表示有m组测试数据;
每组测试数据有一个正整数n<1000000;
输出
如果符合条件,输出Yes,否则输出No;

分析:1000000对于阶乘而言并不大,20以内的阶乘结果一定能大于他。可以先打个表,然后针对每一个数字寻找不大于它的最大值,数字被这个最大值减,这样更新下去。看最后的结果是否是0。

#include <stdio.h>
#include <stdlib.h>
#define Max 1000000
int fac[20];
int midfind(int high,int val){
int l = 1, r = high;
int ans = 1, m;
while(l<=r){
m = (l+r)/2;
if(fac[m] > val){
r = m-1;
ans = m;
}
else l=m+1;
}
return ans-1; //zero means no one <= val.
}
int main(){
int i, ret = 1, top = 0;
for(i=1;i<20;i++){
ret = ret*i;
if(ret<Max) {
fac[i] = ret;
top++;
}
else break;
}
fac[++top] = Max;

int n,m;
scanf("%d",&m);
while(m--){
scanf("%d",&n);
int top1 = top;
int dex = midfind(top1,n);
while(dex > 0){
n = n-fac[dex];
top1 = dex;
dex = midfind(top1,n);
if(dex == top1) break;
}
if(n == 0) puts("Yes");
else puts("No");
}
return 0;
}