程序设计与算法(二)问题记录

1.首先是C++里使用scan()函数和printf()函数,分别是格式化输入和格式化输出,C语言风格函数,用法有点像python
要先导入标准库头文件#include
调用格式
scanf("<格式化字符串>", <地址表>);
printf("<格式化字符串>", <参量表>);
2.然后是while(cin),常用来处理输入的问题
3.strchr()函数也是C语言风格函数,返回第一个匹配的指针,否则返回NULL
4.然后记录一下解题思路
1)首先解决输入如何存放的问题,比如用n个变量存放或者用数组或者多维数组来存放, 用输入流来读取
2)然后是先规划一个大方向,比如外层遍历,解决输出问题等,需要进行的核心处理可以先放进一个函数里,后来慢慢实现函数
3)最后就是最终核心函数的实现
4.关于sizeof 和 strlen的问题,sizeof返回的是内存大小,对于数组而言:如果静态定义数组,且申请的内存长度确定,那么sizeof就是申请的内存大小,例如char[10] char默认是8位即1字节,因此结果就是10,对于其他多个字节的数据类型,如果要使用它来确定数组的长度(例如int a[10])那么建议使用sizeof(a)/size(int)即可 ;一个要注意的点是 char[] 这个数组会自动在最后加上结束符‘\0‘ 不信的话可以试试 char a[4] = “abcd”,看报不报错,对于使用{}进行初始化的方法,例如char a[4] = {‘a’,‘b’,‘c’,‘d’};这样初始化不会报错,但是会导致没有结束符’\0’。这就涉及到了strlen的使用问题,strlen是以’\0’为判断结束的标准,如果char[]中没有’\0’,那么它输出的结果就是不可预知的。

第一周 枚举

称硬币问题
有12枚硬币,其中有11枚真币1枚假币,假币和镇币重量不同但是不知道轻重,找一个天平成了三次,给定结果,求解哪枚是假币并确定假币是轻是重
ABC等字母表示硬币,每行表示每次称重,每行左侧表示天平左边,中间表示天平右边,右边表示结果,even表示等重,up表示右边轻,down表示右边重
输入样例
ABCD EFGH even
ABCI EFJK up
ABIJ EFGH even

输出
K is light

思路采用枚举法,枚举每一个硬币,和轻重情况,带入每个条件观察是否矛盾
C++实现

#include 
#include 
using namespace std;
char Left[3][7];
char Right[3][7];
char Result[3][7];
bool IsFake(char c,bool light){	
	for(int i=0;i<3;++i){
		char *pLeft;
		char *pRight;
		if(light){
			pLeft = Left[i];
			pRight = Right[i]; 
		}
		else{
			pLeft = Right[i];
			pRight = Left[i];
		}
	switch(Result[i][0]){
		case 'u':
			if(strchr(pRight,c)==NULL)
				return false;
			break;
		case 'e':
			if(strchr(pLeft,c)||strchr(pRight,c))
				return false;
			break;
		case 'd':
			if(strchr(pLeft,c)==NULL)
				return false;
			break;
		}
	}
	return true;
}
int main(){
	for(int i=0;i<3;++i)
		cin>>Left[i]>>Right[i]>>Result[i];
	for(char c='A';c<='L';++c){
		if(IsFake(c,true)){
			cout<

熄灯问题
思路:找特殊的地方枚举(第一行),只要枚举了第一行,其余的就会随之确定
对于0-1问题,使用二进制数来表示,效率高很多,比二维数组方便的多
处理过程中的技巧: 和1异或会变号, 和0异或不变 ; 读取二进制数c的某位, (c>>i)&1
改变某位 参数要传引用,同时判断 如果要改成1,就 c|=(1< 某位反转 参数传引用,c ^= (1< 实现代码

#include 
#include 
#include 
#include 
using namespace std;

char oriLight[5];
char lights[5];
char result[5];

int GetBit(char c,int i){
    return (c>>i) & 1 ;
}

void SetBit(char & c, int i, int v){
    if(v)
        c |= (1<>T;
    for(int t=0;t>s;
                SetBit(oriLight[i],j,s);
            }
        for(int n = 0; n < 64; ++n){
            int switchs = n;
            memcpy(lights,oriLight,sizeof(oriLight));
            for(int i = 0; i<5; ++i){
                result[i] = switchs;
                for(int j=0;j<6;++j){
                    if(GetBit(switchs,j)){
                        if(j>0)
                            FlipBit(lights[i],j-1);
                        FlipBit(lights[i],j);
                        if(j<5)
                            FlipBit(lights[i],j+1);

                    }
                }
                if( i < 4)
                    lights[i+1] ^= switchs;
                switchs = lights[i];
            }
            if(lights[4] == 0){
                OutputResult(t,result);
                break;
            }
        }
    }
}

特殊密码锁
1.多于8位的0-1字符串还是直接用char数组保存吧
2.枚举过程记得开率最简单的枚举的方案,比如枚举第一个,剩下的就会随之确定
3.char字符型,如果值是数字,对应的是ascii码
4.数组作为函数参数,传进去的是指针,因此看作是引用传递
5.读取过程要看清楚输入是什么形式,如果是空格隔开可以用while(cin) 如果不是还是直接用char[]来保存

实现代码

#include 
#include 
#include 
#include
#define N 32
using namespace std;

char input_data[N];
char target[N];
char data[N];
char result[N];
int num_min = 50;

void change(char c[],int j,char v){
    if(v=='1'){
        if(c[j] == '0')
            c[j] = '1';
        else
            c[j] = '0';
    }
}


int main(){
    cin>>input_data>>target;
    int length = strlen(input_data);

    for(int i=0;i<2;++i){
        int temp = 0;
        memcpy(data,input_data,sizeof(input_data));
        result[0] = i+48;
        for(int j=0;j0)
                    change(data,j-1,result[j]);
                change(data,j,result[j]);
                if(j

拨钟问题
枚举选取的方式要多观察,既然选择枚举就别怕,只要时间内就ok
9重循环看似吓人
如果不考虑重复的话就只用考虑0,1就好

#include 
using namespace std;


int main()
{
    int z[10], i[10], sum;
    for(int j=1;j<10;++j)
        cin>>z[j];
    for(i[1]=0;i[1]<4;++i[1])
        for(i[2]=0;i[2]<4;++i[2])
            for(i[3]=0;i[3]<4;++i[3])
                for(i[4]=0;i[4]<4;++i[4])
                    for(i[5]=0;i[5]<4;++i[5])
                        for(i[6]=0;i[6]<4;++i[6])
                            for(i[7]=0;i[7]<4;++i[7])
                                for(i[8]=0;i[8]<4;++i[8])
                                    for(i[9]=0;i[9]<4;++i[9]){
                                        sum = 0;
                                        sum+=(z[1]+i[1]+i[2]+i[4])%4;
                                        sum+=(z[2]+i[1]+i[2]+i[3]+i[5])%4;
                                        sum+=(z[3]+i[2]+i[3]+i[6])%4;
                                        sum+=(z[4]+i[1]+i[4]+i[5]+i[7])%4;
                                        sum+=(z[5]+i[1]+i[3]+i[5]+i[7]+i[9])%4;
                                        sum+=(z[6]+i[3]+i[5]+i[6]+i[9])%4;
                                        sum+=(z[7]+i[4]+i[7]+i[8])%4;
                                        sum+=(z[8]+i[5]+i[7]+i[8]+i[9])%4;
                                        sum+=(z[9]+i[6]+i[8]+i[9])%4;
                                        if(sum==0){
                                            for(int j=1;j<10;++j)
                                                for(;i[j]>0;--i[j])
                                                    cout<

第二周 枚举
汉诺塔问题
这个我之前已经总结过了,虽然看到了还是不会写。。 输入有四个参数 第一个是移动n个盘子,第二个是起点,第二个是中间点,第三个是目标点,移动操作是cout ,把问题简化,每次只考虑每次移动经过的过程
比如n个盘子 每次移动分为3点 1.把n-1个从起点移动到中间点 2.把最后一个从起点移动到目标点 3.把中间的n-1个移动到目标点

#include 
using namespace std;


void hanoi(int n, char source, char mid, char target){
    if(n==1){
        cout<"<"<>n;
    hanoi(n,'A','B','C');
    return 0;
}

N皇后问题

递归解决,先判断结束条件(用的时候别怕用return,他是迭代的,return了也只是结束了它对应的迭代,只要搞明白了结束条件之类的就ok)

#include 
#include 
using namespace std;

int queenpos[100];
int N;

void NQueen(int k){
    int i;
    if(k==N){
        for(i=0;i>N;
    NQueen(0);
    return 0;
}

前缀表达式
迭代思想 感觉有点难啊 不过思想理解了,对于迭代问题,考虑太深就输了,把迭代到里面的当作已知量,只处理当前就好
除此之外,在cstdio/stdio.h 标准库里的printf 类似与python里的print,方便格式化输出
在cstdlib标准库里的 atod atof 等,把字符型转成对应的int float型,也非常好用

#include 
#include 
#include 
using namespace std;

double exp(){
    char s[20];
    cin>>s;
    switch(s[0]){
    case '+': return exp() + exp();
    case '-': return exp() - exp();
    case '*': return exp() * exp();
    case '/': return exp() / exp();
    default: return atof(s);
    break;
    }
}

int main(){
    printf("%1f",exp());
    return 0;
}

全排列
这个我一直都看不明白。。先pass 看懂的时候再说
先放上一个全排列但是没有按照顺序的代码吧

#include 
#include 
using namespace std;
char input_data[8];
int N;

void Swap(char x[],int i, int j){
    char temp = x[i];
    x[i]=x[j];
    x[j] = temp;
    return;
}

void whole_sort(char x[],int n){
    if(n==N){
        for(int j=0;j>input_data;
    N = strlen(input_data);
    whole_sort(input_data,0);
    return 0;
}

2的幂次方表示

问题的关键点两个:第一是找到一个数的2次幂表示,考虑成二进制就好说了(话说刚开始不明白为什么所有数都能写成2次幂,后来一想二进制恍然大悟)
第二是递归求问题,还有就是什么时候放加号+ 需要设置一个flag标志位

#include 
using namespace std;

int data;

int GetBit(int n, int i){
    return (n>>i) & 1;
}

void print(int n){
    int first = 1;

    for(int j=15;j>=0;--j){
        if(GetBit(n,j)){
            if(!first){
                cout<<"+";
            }
            else{
                first=0;
            }
            if(j==0)
                cout<<"2(0)";
            else if(j==1)
                cout<<"2";
            else{
                cout<<"2(";
                print(j);
                cout<<")";
            }
        }
    }
}

int main(){
    cin>>data;
    print(data);
    return 0;
}

爬楼梯问题
记不住就背会,,,最经典的递归问题之一
思想是 首先设到n阶的方法有f(n)种, 爬到第n阶楼梯之前,有两个办法 第一种是 从n-1爬1阶到n 第二种是 从n-2阶爬2阶到n,而已知n-1阶方法有f(n-1)种 n-2方法有f(n-2)种,那么总的方法就有
f(n) = f(n-1) + f(n-2)种 再看结束条件 就是两个 如果n =1 那么就是1种 如果n=2 那么就是2种
就实现了
所以迭代的思想还是找规律 找上次到这次的规律 再找终止条件

#include 
using namespace std;

int N;

int stairs(int n){
    if(n==1)
        return 1;
    if(n==2)
        return 2;
    return stairs(n-1) + stairs(n-2);
}

int main(){
    while(cin>>N){
        cout<

简单的整数划分问题
主要还是要找递推关系、边界条件,找好就很好做

#include 
using namespace std;
int ways(int n,int i)
{
	if( n == 0)
		return 1;
	if( i == 0)
		return 0;
	if( i <= n)
		return ways(n-i,i) + ways(n,i-1);  //用i和不用i的情况。i可以重复使用
	else
		return ways(n,i-1);	
	
}
int main()
{
	int n;
	while(cin >> n) 
		cout << ways(n,n) << endl;
}

第三章 二分查找
找一对数 即找两个加和为m的数
方法一暴力法,二重循环 但是时间复杂度高
方法二 先排序 然后遍历 二分查找
方法三 先排序 ,然后分别从两头移动 如果和>m 就右端左移,否则左端右移

农夫和奶牛问题
使用二分法查找合适的D
排序可以使用< algorithm >库里的 sort()函数 时间复杂度nlogn

派 分派问题
思路写出来了。。例程测试对了但是上传不对 问题出在哪啊…先把自己写的保存一下

#include 
#include 
#include 
#define PI 3.1415927
#define eps 1e-6
using namespace std;

int F;
int p;
int N;

bool isok(int n,int p,double mid, int pai[]){
    double sum = 0;
    for(int i=0;i=p)
            return true;
    }

    return false;
}

int max_v(int N, int p, int total, int pai[]){
    double e = total;
    double s = 0;

    while(e-s>eps){
        double mid = s + (e - s)/2;
        if(isok(N,p,mid,pai))
            s = mid;
        else
            e = mid;
    }
    return e;
}

int main(){
    cin>>N;
    cin>>F;
    p = F+1;
    int *pai = new int[N];
    int s;
    int num = 0;
    while(cin>>s){
        pai[num] = s;
        num++;
    }
    int total = 0;
    int temp =0;
    for(int i=0;ipai[i]*pai[i]? temp:pai[i]*pai[i];
    }
//    cout<

月度开销问题
这是我第一次独立写出来的题目。。。历史性的一刻啊(泪目)感谢二分法
思路是二分法,我理解二分法就是在序列里遍历找最优解的问题,所以首先要确定要找什么,再确定判断条件即可,这题比较明显就是找最大月度开销的最小值,那么首先确定这个最大月度开销的范围,然后再范围里使用二分法,寻找最合适的值即可,判断条件也可以简单考虑成 判断这个值满不满足即可

#include 
using namespace std;

int N,M;
int a[100100];

bool fajo(int n){
    int sum_temp = 0;
    int sum_mon = 1;
    for(int i=0;in)
            return false;
        sum_temp += a[i];
        if(sum_temp>n){
            sum_temp = a[i];
            sum_mon++;
        }
    }
    if(sum_mon>M)
        return false;
    else
        return true;
}

int main()
{
    cin>>N>>M;
    int s;
    int mid;
    int num = 0;
    int sum = 0;
    while(cin>>s){
        a[num] = s;
        ++num;
        sum += s;
    }
    int L=0, R = sum;
    while(L<=R){
        mid = L+(R-L)/2;
        if(fajo(mid))
            R = mid-1;
        else
            L = mid+1;
    }
    cout<

第四章 分治法
类似于二分法,把大的问题分解成小的问题求解

归并排序
思路是每次把要排序列分成两部分,因此函数参数有四个
然后把两部分排完之后,再归并到一起

#include 
using namespace std;
int a[10] = {13,27,19,2,8,12,2,8,30,89};
int b[10];

void merge(int a[],int s, int m, int e, int b[]){
    int temp1 = s, temp2 = m+1, b_temp = 0;
    while(temp1<=m && temp2<=e){
        if(a[temp1]

快速排序
思路是取一个值比如第一个 记为k,然后通过O(n)的操作把序列调整成 k前面的都比k小,k后面的都比k大
然后分治

#include 
using namespace std;

int a[10] = {13,27,19,2,8,12,2,8,30,89};

void Quicksort(int a[],int s, int e){
    int k = a[s];
    int i = s , j = e;
    if(s>=e)
        return;
    while(i!=j){
        while(i=k)
            --j;
        swap(a[i],a[j]);
        while(i

堆排序这个不是二分法的内容,既然都写到排序了干脆就加上堆排序吧
堆排序,函数实现的是,先从有子节点的第一个节点开始逐一往根节点遍历,每次都下滤,遍历完之后形成最大堆,然后从最后一个节点开始遍历一直到不是根节点的第一个节点,交换它和根节点,然后从根节点在去掉最后一个节点的堆中下滤,为新根节点找到合适的位置,最后堆内存的那个数组就是排好序的数组

//下滤函数,这个函数相当于是在n个元素的堆(数组)中,从i结点开始下滤,为i节点找到合适的位置
void MaxHeapFixDown(int a[],int i, int n){
    int j = 2*i+1;
    int temp = a[i];
    while(ja[j])
            ++j;
        if(temp>a[j])
            break;
        else{
            a[i] = a[j];
            i = j;
            j = 2*j +1;
        }
    }
    a[i] = temp;
}

//堆排序
void heapsort(int a[],int n){
    for(int i=n/2-1;i>=0;--i){   //先从最后一个有孩子的节点开始遍历到根节点,每次都执行下滤,就形成了最大堆
        MaxHeapFixDown(a,i,n);
    }
    for(int i=n-1;i>=1;--i){  //然后开始逐个从最大堆中取元素,利用了技巧:每次都把最后一个元素和根节点交换位置,就相当于把最大的节点放好了,然后在新的堆里为新的根节点找到位置
        swap(a[i],a[0]);
        MaxHeapFixDown(a,0,i);
    }
}

求最大m个数问题
令人震惊的是这题我又写出来了,虽然改了很多次,我终于理解分治法是怎么回事了,主要还是要找到递推关系,这些关系可以通过各种操作和调用自己来实现,比如本题
经过的操作,首先考虑把从s到e的部分变成取比某值大的在后,比某值小的在前,那么就要通过快排里的while(while+swap+while+swap)实现,比如称它为移动操作 ,那么上一个问题就考虑成 移动+分类考虑,分的类又分为n==k n>k n已知的,递归过程不要担心,只要找好边界条件不会有问题的。这么考虑的话,只要找到由 操作和调用自己组成的递推关系,然后确定好边界条件即可

同样的方法考虑归并排序,设一个操作叫归并,命名为merge,那么问题变成了 大序列 等于 merge(前半个序列,后半个序列) 这就是递推关系了,merge的操作实现则是whilewhilewhile形式

#include 
#include 
using namespace std;

int N;
int a[100100];
int k;

void move(int a[], int s, int e, int k){
    if(s>=e)
        return;
    int t = a[s];
    int i = s;
    int j = e;
    while(ia[i])
            ++i;
        swap(a[i],a[j]);
    }
    if((e-i+1)==k)
        return;
    else if(e-i+1>k)
        move(a,i+1,e,k);
    else{
        move(a,s,i-1,k-e+i-1);
    }
}

int main()
{
    cin>>N;
    for(int i;i>a[i];
    cin>>k;
    move(a,0,N-1,k);
    sort(a+N-k-1,a+N);
    for(int j=N-1;j>=N-k;--j)
        cout<

求逆序数问题
用分治法求解,其实就是归并排序加1行,在归结的时候,算一下当前逆序的个数(这里不用担心重复计算,因为两个要归结的数组已经都排好序了,而他们排序过程中的逆序数已经被计算,并且已经被排成正常序列了)

#include 
using namespace std;

int a[6] = {2,6,3,4,5,1};
int b[6];
int sum = 0;

void merge(int a[],int s, int m, int e, int b[]){
    int i = s , j = m+1;
    int temp = 0;
    while(i<=m && j<=e){
        if(a[i]

第六周 动态规划
感觉这动态规划有点难以理解了。。。涉及递归之类的总觉得有点复杂

数学三角形问题
递归版动态规划,就是在递归的基础上 保存以下状态的值,使得下次调用的时候不用再递归计算,而是直接取出结果,大幅度减少了重复计算
动态规划递归形式

#include 

#define MAX 101
using namespace std;


int D[MAX][MAX];
int n;
int maxSum[MAX][MAX];

int MaxSum(int i, int j){
    if(maxSum[i][j] != -1)
        return maxSum[i][j];
    if(i==n)
        maxSum[i][j] = D[i][j];
    else{
        maxSum[i][j] = max(MaxSum(i+1,j),MaxSum(i+1,j+1))+D[i][j];
        }
    return maxSum[i][j];
}

int main(){
    int i,j;
    cin>>n;
    for(i=1;i<=n;++i)
        for(j=1;j<=i;++j){
            cin>>D[i][j];
            maxSum[i][j] = -1;
        }
    cout<< MaxSum(1,1) <

数学三角形
动态规划递推形式

#include 
#define MAX 101
using namespace std;

int n;
int maxSum[MAX];
int D[MAX][MAX];

int main(){
    cin>>n;
    for(int i=1;i<=n;++i){
        for(int j=1;j<=i;++j)
            cin>>D[i][j];
    }

    for(int i=1;i<=n;++i)
        maxSum[i] = D[n][i];
    for(int i=n-1;i>=1;--i)
        for(int j=1;j<=i;++j){
            maxSum[j] = max(maxSum[j],maxSum[j+1]) + D[i][j];
        }
    cout<

求最长上升子序列问题
经典动态规划问题,步骤是先分解出子问题,然后获取状态、状态转移方程、边界条件,然后根据状态转移方程,要么采用递归、要么采用逆向递推(逆向递推好一些,也好理解),最后解出问题

#include 
#define MAX 1010
using namespace std;

int N;
int a[MAX];
int Maxlen[MAX];
int temp_max = 0;

int main()
{
    cin>>N;
    for(int i=1;i<=N;++i){
        cin>>a[i];
        Maxlen[i] = 1;
    }
    for(int i=2;i<=N;++i)
        for(int j=1;jtemp_max)
            temp_max = Maxlen[i];
    cout<

最长公共子序列问题
这题可以说是相当有难度了,首先它的子问题不好找,其次还是二维状态,是个二维数组,那么递推的时候可以考虑从左到右或者从右到左

#include 
#include 
#define MAX 1000
using namespace std;

char s1[MAX];
char s2[MAX];
int Maxlen[MAX][MAX];

int main(){
    while(cin>>s1>>s2){
        int m = strlen(s1);
        int n = strlen(s2);
        for(int i=0;i<=m;++i)
            Maxlen[i][0] = 0;
        for(int j=0;j<=n;++j)
            Maxlen[0][j] = 0;
        for(int i=1;i<=m;++i){
            for(int j=1;j<=n;++j)
                if(s1[i-1]==s2[j-1])
                    Maxlen[i][j] = Maxlen[i-1][j-1]+1;
                else
                    Maxlen[i][j] = max(Maxlen[i][j-1],Maxlen[i-1][j]);
        }
        cout<

拦截导弹问题
这个就是求最长降序序列,很经典的DP问题

#include 
using namespace std;

int N;
int a[30];
int maxlen[30];

int main(){
    cin>>N;
    for(int i=0;i>a[i];
        maxlen[i]=1;
    }
    for(int i=1;i=a[i]){
                maxlen[i] = max(maxlen[i],maxlen[j]+1);
            }
        }
    int temp = 0;
    for(int i=0;i

ZIPPER问题
刚开始完全搞不明白,最后发现还是要找状态、状态转移方程,然后按部就班DP求解,这里看了一晚上才找出问题所在,要理解清楚设置的状态的下标,不要和数组的下标搞混了

#include 
#include 
using namespace std;

int N;
char s1[210], s2[210], s[700];
int dp[210][210];

int main(){
    cin>>N;
    for(int k=1;k<=N;++k){
        memset(dp,0,sizeof(dp));
        cin>>s1>>s2>>s;
        int lens1 = strlen(s1);
        int lens2 = strlen(s2);

        for(int m=0;m<=lens1;++m)
            for(int j=0;j<=lens2;++j){
                if(m==0 && j==0)
                    dp[m][j] = 1;
                else if(m==0 && j!=0){
                    if(dp[m][j-1]==1 && s2[j-1]==s[j-1])
                        dp[m][j] = 1;
                    else
                        dp[m][j] = 0;
                }
                else if(j==0 && m!=0){
                    if(dp[m-1][j]==1 && s1[m-1]==s[m-1])
                        dp[m][j] = 1;
                    else
                        dp[m][j] = 0;
                }
            }
        for(int i=1;i<=lens1;++i)
            for(int j=1;j<=lens2;++j){
                if(dp[i-1][j]==1 && s1[i-1]==s[i+j-1])
                    dp[i][j] = 1;
                else if(dp[i][j-1]==1 && s2[j-1]==s[i+j-1])
                    dp[i][j] = 1;
                else
                    dp[i][j] = 0;
            }
        if(dp[lens1][lens2]==1)
            printf("Data set %d: yes\n",k);
        else
            printf("Data set %d: no\n",k);
        }
    return 0;
}

滑雪问题
这题真的受益匪浅,感觉动态规划理解更深刻了,不管是迭代求解还是递推求解,都是找到状态转移方程,对于递归求解,只要边界条件找好,直接调用就行,对于递推,由于是逆向推导,还要找好推导方向(因为是由已知推未知),之前的题目都是前项直接推后项,前向肯定比后项先知道,但是本题问题在于数字是二维乱序的,递推过程是由小推到大,而不是按序号方向推,因此就要预先排出一个推导顺序的序列(思路就是建立一个数据结构,把序号按照值的大小顺序排序,再按排序依次取出序列)
本题的思路就是,想要递推求解,首先分解子问题,然后找到状态(就是从i,j点出发的最长子序列),找到状态转移方程、边界条件(边界条件在递推方法中体现在保存的矩阵初始值上),然后开始递推求解。
本题看题+思路花了两个小时。。。实际解决倒很快,写出来一遍过,一个小技巧是可以用sort()对自定义数据结构排序,自己定义以下sort()的第三个参数就可以了。还有就是sort()结尾是尾后指针,要指向最后一个元素的下一个位置,对于vector等容器就是end,但是用数组的时候要注意以下

#include 
#include 
using namespace std;

int line,cow;
int a[200][200];
int maxlen[200][200];

struct abc
{
    int a;
    int b;
    int c;
};

abc b[40000];

bool comp(abc a, abc b){
    return a.c>line>>cow;
    int num = 0;
    for(int i=0;i>a[i][j];
            maxlen[i][j] = 1;
            b[num].a = i;
            b[num].b = j;
            b[num].c = a[i][j];
            ++num;
        }
    sort(b,b+(line*cow),comp);
    for(int i=0;ia[m][n-1])
            max_num = max(max_num,maxlen[m][n-1]);
        if(a[m][n]>a[m-1][n])
            max_num = max(max_num,maxlen[m-1][n]);
        if(a[m][n]>a[m+1][n])
            max_num = max(max_num,maxlen[m+1][n]);
        if(a[m][n]>a[m][n+1])
            max_num = max(max_num,maxlen[m][n+1]);
        maxlen[m][n] = max_num + 1;
    }
    int temp_max = 0;
    for(int i=0;imaxlen[i][j]? temp_max:maxlen[i][j];
        }
    cout<

神奇的口袋
这题看起来不难但是找状态真的好难啊,状态为j种物体凑出体积i的方法数ways(i,j),状态转移的思路是,前j-1种物体凑够i的种类数加上前j-1种没凑够i而且加上i正好能凑够的种类数 就是i,j的种类数,所以要引入体积i,所以这是一个二维动态规划问题。。。看似是一维但是找不到对应的状态描述

#include 
#include 
using namespace std;

int N;
int a[30];
int ways[50][30];

int main(){
    cin>>N;
    memset(ways,0,sizeof(ways));
    for(int i=1;i<=N;++i){
        cin>>a[i];
        ways[0][i] = 1;
    }
    ways[0][0] = 1;
    for(int i=1;i<=40;++i)
        for(int j=1;j<=N;++j){
            ways[i][j] = ways[i][j-1];
            if(i>=a[j])
                ways[i][j] += ways[i-a[j]][j-1];
        }
    cout<

笔试题
程序设计与算法(二)问题记录_第1张图片

#include 
#include 
using namespace std;

int y,m,w,d;

int main(){
    cin>>y>>m>>w>>d;
    int month[] = {31,28,31,30,31,30,31,31,30,31,30,31};
    int month_run[] = {31,29,31,30,31,30,31,31,30,31,30,31};
    int yy,mm;
    if(y == 2000)
        yy = (((y-2000)/4* (365+365+365+366)+ (y-2000)%4 * 365)%7+6)%7;
    else
        yy = (((y-2000)/4* (365+365+365+366)+ (y-2000)%4 * 365)%7+7)%7;
    int  count = (((y-2000) / 100)/4 * 3) + ((y-2000) / 100)%4  ;

    yy = yy-count;
    mm = 0;
    if((y/4 == 0 && y/100 != 0) || y%400 ==0 )
        for(int i=0;i d){
            cout<< 0 < month_run[m-1]){
            cout<<0< month[m-1]){
            cout<<0<

你可能感兴趣的:(程序设计与算法(二)问题记录)