还有一篇专门记录贪心和dp的,我就懒得合并了,两篇算到一起看就好。
窝觉得自己好几把菜,而且做的题太少了,这一篇博客长期更新,自己会有意识的去做各种经典问题,可能会一次又一次的认识自己有多么菜,但是坚持下去。
所以题目的代码都可以自己去对应oj的账号上去看, 或者在桌面的题集txt里面也有代码
2016-4-11
http://acm.pku.edu.cn/JudgeOnline/problem?id=1014
用其他思维去想的话会很麻烦,而且感觉自己的数学水平明显不够用,所以必然用最简单的dfs去暴力做,这个地方不加一个限制条件会wa,就是要判断tem+i<=sum/2,才能进入下一层dfs。
此题可以用母函数做,所以拓展一下母函数的经典题型:
4-12
http://acm.pku.edu.cn/JudgeOnline/problem?id=1042
哇草,明明知道是贪心,但是窝还是想了挺久,还是灵性一闪 ,才想到了,真是菜。
只有一个关键点: 遍历这个人最后一个钓鱼的湖就好了,因为他不可能来来回回走,然后其他时间肯定在钓鱼,就这么简单啊,至于那个要在lake1 花的时间多,我们只要让相同的其他湖无法更新lake1就好了。
http://acm.pku.edu.cn/JudgeOnline/problem?id=1037
非常经典的dp,但是不能否认相当难写。
首先:理解题意,推出状态转移方程是第一步:
题意一句话概括: 输入n,c : 求出1-n 的符合题意(除了a1,a2 之外的所有 ai 比ai-1 和 ai+1 都要高,或者都要低) 的字典序 排列的第c 个排列方式。
感觉挺有难度,想了半天,并不会,无奈的去看题解:
对这n个木条进行排序,结果无非有下面n类: 以S1开头,以S2开头…以Sn开头。按照题目要求对这n类进行排序。
S1 。。。
S2 。。。
。。。。
Sn 。。。
举个例子吧。如果以S1开头的有10种排列,以S2开头的有10种排列…现在有n个木条,如果要确定第15个排列,那么就可以知道第15个排列必然是以S2开头的。因为已经知道了最终排列的第一个木条是S2,那么只要依次求出后面n-1个木条即可。如果能把S2开头的区间再划分成若干个区间,并且知道每个区间的大小就和上面的问题一样了。
S2 S1 。。。
S2 S3 。。。
。。。
S2 Sn 。。。
这时问题就转换为:有n-1个木条,现在要确定第5个排列(15-10=5,以S1开头的排列已占去了10个),然后依次类推 就并不难理解。
结合题意:
现在结合题目,题目要求出现波浪形,即有高度变换。这时以Si开头的排列又可以分成两类:第二个木条比Si高,我们记做Hi, 第二个木条比Si低,我们记做Li。这时区间细分成如下形式:
L1 。。。
H1 。。。
L2 。。。
H2 。。。
。。。。。。
Ln 。。。
Hn 。。。
要确定最终的排列,关键是要知道每个区间里排列的个数。
那么现在只能能够求出区间里面的排列个数,就意味着这道题解出来了。
假设我们可以知道每一种排列方式的种类,那么我们就可以判断出我们要求的第c种排列方式落在了上述的哪个区间,这样我们就可以把第一根木条确定下来。规模较小到了1~n-1,若木条落在L方式下,我们就找下一个的H方式;若木条是落下H方式,我们就找下一个的L方式,用同样的方法确定第二根木条,然后以此类推,求出所有的木条,问题就可以解决了!但是别急,貌似其中还有一些问题。当我们确定了一根木条,去找第二根木条时,剩下 的木条的长度就是不连续的了,子问题与原问题是同一类问题吗?其实不用担心,原问题是1~n个长度不同的木条以H或者L方式排列的个数,我们只是为了方便记录状态,而将他们的长度规定为1~n,即使它们的长度是不连续的,问题的本质还是一样的。我们可以这样想:我们在第一步确定了1~n个木条中的第x个,那么我们在寻找下一根木条时,事先可以将(x+1)~n的木条长度减1,这样长度就变成了长度为1~(n-1)的n-1根木条的排列问题了。
我们可以设H[x][n]为由n个长度不同的木条组成的栅栏中,以其中第x根木条开始的“H”型排列的个数,L[x][n]为由n个长度不同的木条组成的栅栏中,以其中第x根木条开始的“L”型排列的个数。可以推出以下公式:
H[x][n] = ∑L[i][n-1] (1<=i<=x-1)
L[x][n] = ∑H[i][n-1] (x<=i<=n-1)
实在是写得太挫了,真的代码能力真的好弱。在题集里面贴了自己的代码。
4-13
经典数论题,2015年上海现场赛题
http://acm.hdu.edu.cn/showproblem.php?pid=5584
想了许久,还是熊神强行找了一波规律,时候看了看大神写的博客:
http://blog.csdn.net/queuelovestack/article/details/50094499 顿时觉得我们好弱!!!
简单来说 前一个点为 (x,y) gcd(x,y)=k,那么可以表示为(m1 * k ,m2 * k); 所以 Lcm = m1*m2 *k;
所以往上 可以为:(m1*k, m2 * k+m1 * m2 * k);
往右 为: (m1*k+m1 * m2 * k, m2 * k);
而 gcd(m1,m2)必然等于 1, gcd(m1,m1+1) 必然也等于1
所以 gcd(m1*k+m1 * m2 * k, m2 * k) =gcd(m1 *k(m2+1) , m2 *k)=k ; 所以后一个点的gcd=前一个点的gcd,由此自然就可以求出一切。 代码: 我是用手搓的dfs
.
.
.
.
.
5-31 经典简单贪心 删除数字:
Description
面对任意一个整数,如果我们需要删除掉其中的几位,怎样才能保证输出的数值最小呢?
Input
输入的第一行包含一个正整数,数字的总位数不超过1000位;第二行包含一个正整数n。表示要从第一行的数值中删除n位数字(0 < n < 1000)
Output
输出从输入的数值中删掉n位后能够产生的最小整数
注意结果不能有前置的零。
Sample Input
1372123
3
Sample Output
1123
如果xjb去搞 还是会有一点麻烦的,这种题就是: 动手之前不去想,肯定血崩, 想完了动手去写,用的方法不太好,也是血崩。 这种简单题拖时间太久 就是绝对的血崩。
不废话了,如果对一串数字,abcdefgh ,我们删掉x个之后 还会剩下 len-x,
先想清楚 这个剩下的数字大小是由什么决定的? 优先级必然 1位>2位>3位>4位….
也就是说我们没有去要删除最大的,比如: 2 1 9 0 这一串数字删除一个 最小多少? 很简单的可知道是190, 不会因为9比较大而就去删掉9
由此,我们可以得到贪心的规则: 由高位到地位,我们若存在str[i]>str[i+1],我们优先删除第一个满足这个条件的i,也就是优先满足高位。
关键的问题是这个地方 用char 会写的比较麻烦,甚至数据可以卡Tle。
而用stl 的string 就不会了,自己去记录下string的各个函数,浪哥:“stl是非常非常有用的,一定要善用。”
如果出现 1555 我们就直接退出,然后输出前l-x位就行了,还有前导0 就自己处理处理就行了。
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<stdlib.h>
#include<queue>
#include<stack>
#include<map>
#include<vector>
#define mem(a) memset(a,0,sizeof(a))
#define INF 0x7fffffff //INT_MAX
#define inf 0x3f3f3f3f //
const double PI = acos(-1.0);
const double e = exp(1.0);
template<class T> T gcd(T a, T b) { return b ? gcd(b, a % b) : a; }
template<class T> T lcm(T a, T b) { return a / gcd(a, b) * b; }
bool cmpbig(int a,int b){return a>b;}
bool cmpsmall(int a,int b){return a<b;}
using namespace std;
const int N = 20005;
int main(){
freopen("1.txt","r",stdin);
string str;
while(cin>>str){
int step;
scanf("%d",&step);
int i=0;
int l=str.length();
int x=step;
while(step){
if(str[i]>str[i+1]){
str=str.erase(i,1); //string &erase(int pos = 0, int n = npos);//删除pos开始的n个字符,返回修改后的字符串
i=-1;
step--;
}
if(i==str.length()-2 && step>0){ // 这个时候没有递减了,那就删掉后面几个就好了
break;
}
i++;
}
//只能输出 前 l -step 个
if(l-x<0) {printf("0\n");continue;}
int cnt=0;
for(i=0;i<l-x;i++){
if(str[i]!='0'){
printf("%c",str[i]);
cnt=1;
}
if(str[i]=='0'&&cnt)
printf("%c",str[i]);
}
if(cnt==0){printf("0\n"); continue;}
cout<<endl;
}
return 0;
}
另外:
有个数据加强版的 n有10^6 那么长,所以O(n*n)g过不了,我修改了下,但是貌似因为 string 里面的erase函数就是一个On 的时间复杂度,所以并不能过。
水题?但是为什么我想了很久,并不能推出这个东西的正确性?
看了别人的证明 ,好像又觉得不难,我好菜啊!
我自己想的可能和别人写的稍有不同但思路是一样的:
.top
.
.. W_sum.
.
.wi+1 si+1 ……. risk:W_sum-si+1 ①……. …….交换 i和i+1 ->……. wi si…………..risk: W _ sum-si ③
.
.wi si…………….. risk:W_sum+wi+1-si ②……. …………………->……. wi+1 si+1…..risk:W _sum+wi-si+1 ④
.
.
若我们原始是最优解,那么我们应该存在 交换完之后的i 和 i+1 处的risk 值都要大于原 序列中的risk
即: 式 1 < 式3 && 式子2 <式子4
W_sum-si+1①< W _ sum-si ③ …………………=》 si+1< si
W_sum+wi+1-si ② < W _sum+wi-si+1 ④…..=》 wi+1 +si+1 < wi+si
由此可知我们想要得到更优解 , 把那些w +s 大的往下面放,一个快排就可以解决了。 TUT我好菜啊
poj 3045
Description
Farmer John养了N(1<=N<=50,000)头牛,她们已经按1~N依次编上了号。FJ所
不知道的是,他的所有牛都梦想着从农场逃走,去参加马戏团的演出。可奶牛们
很快发现她们那笨拙的蹄子根本无法在钢丝或晃动的的秋千上站稳(她们还尝试
过把自己装在大炮里发射出去,但可想而知,结果是悲惨的)。最终,她们决定
练习一种最简单的杂技:把所有牛都摞在一起,比如说,第一头牛站在第二头的身
上,同时第二头牛又站在第三头牛的身上…最底下的是第N头牛。(牛果然没什么
创造力)每头牛都有自己的体重以及力量,编号为i的奶牛的体重为W_i(1<=W_i<=10,000),
力量为S_i(1<=S_i<=1,000,000,000)。当某头牛身上站着另一些牛时它就会在一定
程度上被压扁,我们不妨把它被压扁的程度叫做它的压扁指数。对于任意的牛,她
的压扁指数等于摞在她上面的所有奶牛的总重(当然不包括她自己)减去它的力量。
奶牛们按照一定的顺序摞在一起后,她们的总压扁指数就是被压得最扁的那头奶牛
的压扁指数。你的任务就是帮助奶牛们找出一个摞在一起的顺序,使得总压扁指数
最小。
Input
- 第1行: 一个单独的正整数N
- 第2..N+1行: 第i+1行给出编号为i的奶牛的体重与力量W_i和S_i,用一个空格隔开
Output
- 第1行: 一个整数,表示奶牛们总压扁指数的最小值
Sample Input
3
10 3
2 5
3 3
Sample Output
2
HINT
把重量为10的那头牛放在最底下,于是她的压扁指数就是2+3-3=2。其他2头
牛的压扁指数都小于这个值。
虽然不知道这种灵性是怎么来的,但是这种贪心好像很正确的样子 TUT ,我好菜啊
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<stdlib.h>
#include<queue>
#include<stack>
#include<map>
#include<vector>
#define mem(a) memset(a,0,sizeof(a))
#define INF 0x7fffffff //INT_MAX
#define inf 0x3f3f3f3f //
const double PI = acos(-1.0);
const double e = exp(1.0);
template<class T> T gcd(T a, T b) { return b ? gcd(b, a % b) : a; }
template<class T> T lcm(T a, T b) { return a / gcd(a, b) * b; }
bool cmpbig(int a,int b){return a>b;}
bool cmpsmall(int a,int b){return a<b;}
using namespace std;
const int N = 50010;
struct mou{
int w,s;
}f[N];
bool cmp(mou a,mou b){
return a.w+a.s > b.w+b.s;
}
int main(){
freopen("1.txt","r",stdin);
int n;
while(~scanf("%d",&n)){
int sum=0;
for(int i=1;i<=n;i++){
scanf("%d %d",&f[i].w,&f[i].s);
sum+=f[i].w;
}
sort(f+1,f+n+1,cmp);
int ans=0;
for(int i=1;i<=n;i++){
sum-=f[i].w;
int cnt=sum-f[i].s;
// ans==0?ans=cnt:ans=max(ans,cnt); // 比如说: 1 0 -1 这样的话 最后结果就是-1 GG
i==1?ans=cnt:ans=max(ans,cnt);
}
printf("%d\n",ans);
}
return 0;
}