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<
#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<