NOIP模拟题[贪心][离散化][LIS]
2016-11-06 22:01
274 查看
思考深入再深入!
小心MLE(躺
T1:
题意:
有n个怪物,生命值分别是hi,你有M魔法值,魔法值可用来使某一怪物失去2点生命值或所有怪物失去1生命值。每一轮你可以选择是否用魔法值(若不用则使一怪物失1血),在你做出选择后,所有生命值严格大于一的怪物会分别使你失去1生命值,求怎样决策能使得你失去的生命值最少。
分析:
首先,肯定优先攻击生命值低的怪物,因为这样绝不会使你被攻击的次数增多。(不管怎样,全被打死的时间一定,第一个被打死的时间不会更短)。
然后考虑若存活怪物数大于等于3,则发动群技,因为这样两次过后,对于未死亡的怪物来说,造成的影响至少等于每人遭受一次重击,显然更优。这样可以让后来少受至少死亡怪物多攻击的数量2倍的攻击数。
最后很简单,魔法值能用肯定用,但如果只剩一生命值那用不用都一样还是节约着吧。
T2:
题意:
在一块很大的矩阵上面每次朝一个方向走很长一步路并把经过的所有格子都做标记,求被被标记的格子围成的面积大小。
分析:
一眼离散化,具体怎么离散:
首先把经历过的横纵坐标及其加1减一情况都存下来并排序,就得到了离散后坐标。
然后在离散后的矩阵上走,标记。
然后仍然从边界dfs,但是由于外围的点并未明确地赋予离散前的对应坐标,这里的答案要再扫一次地图得到未被走过的格子来得到。
注意,由于边界也算是被围住,所以一并处理。
调试中遇到的问题:在定义数组用到Maxn以后,不要再在边界处调用Maxn,因为肯定访问无效内存。
局部变量记得赋值为0;
T3:
题意:
给你一个循环t次的n长的序列,求最长上升序列。
分析:
首先,如果最大的数的大小小于t,则一定能选完。
然后,如果循环次数大于n,则一定能选完。
所以,采用二分的LIS,这道题成功缩小到2e8带Log,时间6s,卡一卡常数能满心血泪地过。
顺便:我一直认为直接从长向短枚举,遇见可以就break也是log,后来发现没有理论依据并且可以举出反例。
小心MLE(躺
T1:
题意:
有n个怪物,生命值分别是hi,你有M魔法值,魔法值可用来使某一怪物失去2点生命值或所有怪物失去1生命值。每一轮你可以选择是否用魔法值(若不用则使一怪物失1血),在你做出选择后,所有生命值严格大于一的怪物会分别使你失去1生命值,求怎样决策能使得你失去的生命值最少。
分析:
首先,肯定优先攻击生命值低的怪物,因为这样绝不会使你被攻击的次数增多。(不管怎样,全被打死的时间一定,第一个被打死的时间不会更短)。
然后考虑若存活怪物数大于等于3,则发动群技,因为这样两次过后,对于未死亡的怪物来说,造成的影响至少等于每人遭受一次重击,显然更优。这样可以让后来少受至少死亡怪物多攻击的数量2倍的攻击数。
最后很简单,魔法值能用肯定用,但如果只剩一生命值那用不用都一样还是节约着吧。
#include<cstdio> #include<iostream> #include<cmath> #include<cstdlib> #include<cstring> #include<string> #include<algorithm> #include<queue> #include<set> #include<map> #include<stack> #include<vector> #include<ctime> #define ll long long #define inf 2e8 #define modd 1e9+7 #define clr(x) memset(x,0,sizeof(x)) #define maxen(x) memset(x,127,sizeof(x)) #define maxer(x) memset(x,31,sizeof(x)) #define minus(x) memset(x,-1,sizeof(x)) #define each(i,n) for(int i=1;i<=n;i++) #define minn(a,b,c) min(a,min(b,c)) #define maxx(a,b,c) max(a,max(b,c)) #ifdef WIN32 #define lld "%I64d" #else #define lld "%lld" #endif #define PROC "zhijian" //for(int i=1;i<=n;i++) //(double) (ll) LL (int) //(double)clock()/CLOCKS_PER_SEC using namespace std; const int Maxn=1e5+5; int a[Maxn]; int n,mlef,ach,cur=1,qunji; int ans=2e9,tot; int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } void init() { n=read();mlef=read(); each(i,n)a[i]=read(); sort(a+1,a+n+1); } void work() { while(n-cur+1>=3&&mlef){ int tmp=min(a[cur]-qunji,mlef); qunji+=tmp; mlef-=tmp; ach+=(tmp-1)*(n-cur+1); if(!(a[cur]-qunji))cur++; ach+=n-cur+1; } while(n-cur+1){ int tmp=min((a[cur]-qunji)/2,mlef); a[cur]-=tmp*2; mlef-=tmp; tmp+=a[cur]-qunji; ach+=(tmp-1)*(n-cur+1); cur++; ach+=n-cur+1; } printf("%d",ach); } void debug() { // } int main() { freopen(PROC".in","r",stdin); freopen(PROC".out","w",stdout); init(); work(); //debug(); return 0; }
T2:
题意:
在一块很大的矩阵上面每次朝一个方向走很长一步路并把经过的所有格子都做标记,求被被标记的格子围成的面积大小。
分析:
一眼离散化,具体怎么离散:
首先把经历过的横纵坐标及其加1减一情况都存下来并排序,就得到了离散后坐标。
然后在离散后的矩阵上走,标记。
然后仍然从边界dfs,但是由于外围的点并未明确地赋予离散前的对应坐标,这里的答案要再扫一次地图得到未被走过的格子来得到。
注意,由于边界也算是被围住,所以一并处理。
调试中遇到的问题:在定义数组用到Maxn以后,不要再在边界处调用Maxn,因为肯定访问无效内存。
局部变量记得赋值为0;
#include<cstdio> #include<iostream> #include<cmath> #include<cstdlib> #include<cstring> #include<string> #include<algorithm> #include<queue> #include<set> #include<map> #include<stack> #include<vector> #include<ctime> #define ll long long #define inf 2e8 #define modd 1e9+7 #define clr(x) memset(x,0,sizeof(x)) #define maxen(x) memset(x,127,sizeof(x)) #define maxer(x) memset(x,31,sizeof(x)) #define minus(x) memset(x,-1,sizeof(x)) #define each(i,n,m) for(int i=n;i<m;i++) #define minn(a,b,c) min(a,min(b,c)) #define maxx(a,b,c) max(a,max(b,c)) #ifdef WIN32 #define lld "%I64d" #else #define lld "%lld" #endif #define PROC "luobo" //for(int i=1;i<=n;i++) //(double) (ll) LL (int) //(double)clock()/CLOCKS_PER_SEC using namespace std; const int Maxn=3005; const int zl[4][2]={{1,0},{-1,0},{0,1},{0,-1}}; int mapy[Maxn][Maxn],dx[Maxn],dy[Maxn]; int stac[Maxn*Maxn][2],head,trans[30],dir[Maxn],x,y; int n,curx,cury,ste,tox,toy; char opt='R'-'A'; struct node { int x,y; }p[Maxn]; int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } void disp() { sort(dx+1,dx+3*n+4); sort(dy+1,dy+3*n+4); dx[0]=unique(dx+1,dx+3*n+4)-dx; dy[0]=unique(dy+1,dy+3*n+4)-dy; } void init() { n=read(); trans['L'-'A']=1; trans['U'-'A']=2; trans['D'-'A']=3; each(i,0,n+1){ dir[i]=trans[opt]; p[i].x=(curx+=zl[dir[i]][0]*ste); p[i].y=(cury+=zl[dir[i]][1]*ste); each(j,-1,2){ dx[3*i+j+2]=curx+j; dy[3*i+j+2]=cury+j; } if(i!=n){ opt=getchar(); while(opt>='Z'||opt<='A') opt=getchar(); opt-='A'; ste=read(); } } disp(); curx=p[0].x=lower_bound(dx+1,dx+dx[0],p[0].x)-dx; cury=p[0].y=lower_bound(dy+1,dy+dy[0],p[0].y)-dy; each(i,1,n+1){ p[i].x=lower_bound(dx+1,dx+dx[0],p[i].x)-dx; p[i].y=lower_bound(dy+1,dy+dy[0],p[i].y)-dy; while(curx!=p[i].x||cury!=p[i].y){ mapy[curx][cury]=2; curx+=zl[dir[i]][0]; cury+=zl[dir[i]][1]; } } mapy[curx][cury]=2; } void dfs(int xx,int yy) { stac[++head][0]=xx; stac[head][1]=yy; mapy[xx][yy]=1; while(head){ x=stac[head][0]; y=stac[head--][1]; for(int i=0;i<4;i++){ tox=x+zl[i][0]; toy=y+zl[i][1]; if(!mapy[tox][toy]){ mapy[tox][toy]=1; stac[++head][0]=tox; stac[head][1]=toy; } } } } void work() { for(int i=0;i<=3001;i++){ mapy[i][0]=1; mapy[i][3001]=1; mapy[0][i]=1; mapy[3001][i]=1; } for(int i=1;i<=3000;i++){ if(!mapy[i][1])dfs(i,1); if(!mapy[i][3000])dfs(i,3000); if(!mapy[1][i])dfs(1,i); if(!mapy[3000][i])dfs(3000,i); } ll cnt=0; each(i,1,3001) each(j,1,3001) if(mapy[i][j]^1) cnt+=((ll)dx[i+1]-(ll)dx[i])* ((ll)dy[j+1]-(ll)dy[j]); printf("%I64d",cnt); } int main() { freopen(PROC".in","r",stdin); freopen(PROC".out","w",stdout); init(); work(); //debug(); return 0; }
T3:
题意:
给你一个循环t次的n长的序列,求最长上升序列。
分析:
首先,如果最大的数的大小小于t,则一定能选完。
然后,如果循环次数大于n,则一定能选完。
所以,采用二分的LIS,这道题成功缩小到2e8带Log,时间6s,卡一卡常数能满心血泪地过。
顺便:我一直认为直接从长向短枚举,遇见可以就break也是log,后来发现没有理论依据并且可以举出反例。
#include<cstdio> #include<iostream> #include<cmath> #include<cstdlib> #include<cstring> #include<string> #include<algorithm> #include<queue> #include<set> #include<map> #include<stack> #include<vector> #include<ctime> #define ll long long #define inf 2e8 #define modd 1e9+7 #define clr(x) memset(x,0,sizeof(x)) #define maxen(x) memset(x,127,sizeof(x)) #define maxer(x) memset(x,31,sizeof(x)) #define minus(x) memset(x,-1,sizeof(x)) #define each(i,n) for(int i=1;i<=n;i++) #define minn(a,b,c) min(a,min(b,c)) #define maxx(a,b,c) max(a,max(b,c)) #ifdef WIN32 #define lld "%I64d" #else #define lld "%lld" #endif #define PROC "dishu" //for(int i=1;i<=n;i++) //(double) (ll) LL (int) //(double)clock()/CLOCKS_PER_SEC using namespace std; const int Maxn=1e5+5; int mapy[Maxn],a[Maxn],f[Maxn]; int k,n,maxa,t,cnt,idx; int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } void init() { k=read();n=read(); maxa=read();t=read(); } void work() { clr(mapy);f[0]=-inf; cnt=idx=0; each(i,n) if(!mapy[a[i]=read()]){ mapy[a[i]]=1;cnt++;} if(t>=n||t>=maxa){printf("%d\n",cnt);return;} each(i,t) each(j,n) if(a[j]>f[idx]){ f[++idx]=a[j]; } else{ int k=lower_bound(f,f+idx+1,a[j])-f; f[k]=a[j]; } printf("%d\n",idx); } void debug() { // } int main() { freopen(PROC".in","r",stdin); freopen(PROC".out","w",stdout); init(); each(i,k) work(); //debug(); return 0; }
相关文章推荐
- NOIP模拟题 2016.11.5 [贪心] [坐标离散化] [循环序列LIS]
- NOIP模拟题 2016.8.29 [树相关问题] [数论] [贪心] [拓扑排序]
- (贪心)NOIP模拟题:引爆炸弹
- NOIP模拟题 2017.7.3 - 模拟 - 贪心 - 记忆化搜索
- 【贪心】NOIP模拟题“Kun”
- [NOIP模拟题][Catalan数][逆元][贪心][线段树][DFS][搜索顺序剪枝]
- NOIP模拟题 2016.11.12 [Catalan数] [贪心] [动态规划] [DLX] [数独]
- NOIP模拟题 [LIS][建图][递推][容斥]
- NOIP模拟题 kun 栈 贪心 解题报告
- NOIP模拟题 [暴力][贪心][栈][dfs][找规律]
- NOIP模拟题 [构造][贪心][暴力]
- 湖南NOIP集训模拟题DAY1 BY ExfJOE [贪心][DP][二分]
- NOIP模拟题 2016.11.1 [模拟] [贪心] [杂题]
- [vijos NOIP模拟题]天神下凡 贪心+搜索
- NOIP模拟题 River Path Word[排序][贪心][DP]
- 【noip模拟题】天神下凡(贪心)
- NOIP模拟题 2016.8.27 [贪心] [DP] [计数问题]
- 【NOIP模拟题】【线段树】【离散化】【DP】2016.11.14第三题 有趣的有趣的家庭菜园 题解
- [NOIP模拟题][杂题][贪心][水题]
- NOIP模拟题 2016.11.15 [LIS] [spfa] [同余最短路] [矩阵快速幂] [容斥原理] [数学]