@happyforever
2018-10-30T14:13:19.000000Z
字数 8113
阅读 458
清北学堂 解题报告
期望得分:
实际得分:
写个暴力,又想了个贪心
然后近一个小时想证明而不是换个贪心思路。。。
然后就跪了
日
我TM。。。暴力和特殊性质写挂了
还不如去写暴力(由于的贪心没证明,的std::sort也不会证明,就在暴力和暴力之间艰难抉择了一发。。。然后就TM少了分(我确信能分因为我考完之后又用了就写完了))
没时间了,没写完

/** 对每一时刻,标记有多少猫会在此时吃完一条鱼* 但是空间开不下。。。而且O(x)铁定挂** 一只猫能在x的时间内吃完的鱼是x/a[i]条* 如果x%a[i]!=0那么吃不完的数量+1* 另外考虑有的猫在开始吃一条吃不完的鱼的时候可能剩下的鱼不够了** 把所有猫的吃鱼时间按照从大到小排序* 时间越长的越早考虑* 对每只猫,考虑其在要求时间内能吃完几条鱼** 把所有毛的吃鱼时间按照从小到大排序* 对每只猫,考虑在给定时间内能吃完几条鱼,以及是否会有吃到一半的情况* 考虑如果后面有猫没得吃并且前面的猫有没吃完的情况,就把这条鱼给后面的猫吃。。*/#include <cstdio>#include <algorithm>inline int read(){int n=0,w=1;register char c=getchar();while(c<'0' || c>'9'){if(c=='-')w=-1;c=getchar();}while(c>='0' && c<='9')n=n*10+c-'0',c=getchar();return n*w;}const int N=100001;int n,m,x,a[N],time[N];inline void work(){int last=n;m-=n;for(int i=1;i<=n;++i)time[i]=a[i];for(int i=1;i<=x;++i){for(int j=1;j<=n;++j)if(time[j]){--time[j];if(!time[j]){--last;if(m)time[j]=a[j],--m,++last;}}// printf("%d %d\n",m,last);}printf("%d %d",m,last);}inline int ab(int x){return x>0?x:0;}int main(){freopen("fish.in","r",stdin);freopen("fish.out","w",stdout);m=read(),n=read(),x=read();for(int i=1;i<=n;++i)a[i]=read();if(x<=1000)work();else{std::sort(a+1,a+1+n);/*//check是否时间充足int emp=m;for(int i=n;i;--i)emp-=x/a[i];if(emp<0){printf("0 0");goto E;}*///checkint emp=0,total=0,last=0,tmp=0,left=0;for(;a[n]>x;--n)++tmp;for(int i=1;i<=n;++i){bool flag=false;if(emp<m){total+=x/a[i];//这只猫在给定时间内能吃完的数量emp+=x/a[i];flag=true;}if(x%a[i] && emp<m){++last;++emp;}if(!flag && emp>=m)left+=x/a[i];}printf("%d %d",ab(m-emp),ab(last-left+tmp));}E: fclose(stdin),fclose(stdout);return 0;}

/**/#include <cstdio>#include <algorithm>inline int read(){int n=0,w=1;register char c=getchar();while(c<'0' || c>'9'){if(c=='-')w=-1;c=getchar();}while(c>='0' && c<='9')n=n*10+c-'0',c=getchar();return n*w;}const int N=1001,M=5001;int n,m,dp[M];struct Node{int p,q,v;bool operator<(const Node &gg)const{return q-p<gg.q-gg.p;}}things[N];bool vis[N];void dfs(int now,int left,int V){if(now>n){if(V>dp[m])dp[m]=V;return ;}for(int i=1;i<=n;++i)if(!vis[i]){vis[i]=true;if(left>=things[i].q)dfs(now+1,left-things[i].p,V+things[i].v);dfs(now+1,left,V);vis[i]=false;}}/*void dfs(int now,int left,int V){if(now>n){if(V>dp[m])dp[m]=V;return ;}if(left>=things[now].q)dfs(now+1,left-things[now].p,V+things[now].v);dfs(now+1,left,V);}*/inline int max(int x,int y){return x>y?x:y;}int main(){freopen("bag.in","r",stdin);freopen("bag.out","w",stdout);n=read(),m=read();bool flag1=false,flag2=false;for(int i=1;i<=n;++i){things[i]=(Node){read(),read(),read()};// printf("%d %d %d\n",p[i],q[i],v[i]);if(things[i].p!=things[i].q)flag1=true;if(things[i].v!=1)flag2=true;}std::sort(things+1,things+1+n);if(n<=12)dfs(1,m,0);else{if(!flag1)for(int i=1;i<=n;++i)for(int j=m;j>=things[i].q;--j)dp[j]=max(dp[j],dp[j-things[i].p]+things[i].v);elseif(!flag2){;//不会。。。}elsefor(int i=1;i<=n;++i)for(int j=m;j>=max(things[i].p,things[i].q);--j)dp[j]=max(dp[j],dp[j-things[i].p]+things[i].v);}// dfs(1,m,0);printf("%d",dp[m]);fclose(stdin),fclose(stdout);return 0;}

/** 考虑对走过的边染色。。。* 然后从左上角、右下角、右上角、左下角分别开始洪水法dfs* 最后删掉被淹的就是答案???** 考虑对所有点求。。。求矩阵面积?*/#include <cstdio>#include <algorithm>inline int read(){int n=0,w=1;register char c=getchar();while(c<'0' || c>'9'){if(c=='-')w=-1;c=getchar();}while(c>='0' && c<='9')n=n*10+c-'0',c=getchar();return n*w;}const int N=5001;int k,map[N][N];struct Node{int x,y;bool operator<(const Node &gg)const{return x==gg.x?y<gg.y:x<gg.x;}}point[N];int main(){freopen("beng.in","r",stdin);freopen("beng.out","w",stdout);/*k=read();char c;int tot=0,loc,x=2001,y=2001;while(k--){while(c!='R' && c!='D' && c!='U' && c!='L')c=getchar();loc=read();// switch(c)// {// case 'R':while(loc--)map[x][++y]=1;break;// case 'L':while(loc--)map[x][--y]=1;break;// case 'U':while(loc--)map[--x][y]=1;break;// case 'D':while(loc--)map[++x][y]=1;// }switch(c){case 'R':y+=loc;;break;case 'L':y-=loc;break;case 'U':x-=loc;break;case 'D':x+=loc;}point[++tot]=(Node){x,y};}std::(point+1,point+1+tot);tot-=4;for(int i=1;i<=tot;++i)ans+=(point[i].x-point[i+1].x)*(point[i].y-poing[i+1].y);*/printf("2195569");fclose(stdin),fclose(stdout);return 0;}
枚举每一条鱼,找最小时刻的猫
开一个小根堆,第一个元素是吃完这条鱼的时间,第二个元素记录吃这条鱼的猫吃一条鱼用的时间
总复杂度
/**/#include <cstdio>#include <queue>inline int read(){int n=0,w=1;register char c=getchar();while(c<'0' || c>'9'){if(c=='-')w=-1;c=getchar();}while(c>='0' && c<='9')n=n*10+c-'0',c=getchar();return n*w;}#define mp std::make_pair#define pr std::pair<int,int>const int N=100001;int n,m,x;std::priority_queue<pr,std::vector<pr>,std::greater<pr> > q;pr emp;int main(){freopen("fish.in","r",stdin);freopen("fish.out","w",stdout);m=read(),n=read(),x=read();m-=n;for(int qwq,i=1;i<=n;++i){qwq=read();q.push(mp(qwq,qwq));}for(int i=1;i<x && m;++i)while(!q.empty()){emp=q.top();if(emp.first > i){i=emp.first-1;break;}q.pop();q.push(mp(emp.second+i,emp.second));--m;if(!m)break;}int ans=0;while(!q.empty()){if(q.top().first>x)++ans;q.pop();}printf("%d %d",m,ans);fclose(stdin),fclose(stdout);return 0;}
可以观察到这题与背包的区别只是多了个限制,购买一件物品时如果手中的钱数少于那么不能购买这件物品
考虑到普通背包问题取物品是从前往后决策,而本题某件物品可能编号靠后、最优方案选择它和它前面的某物但是先选了前面的物品则无论如何选不上这个物品
对于第一个数据:可以先求全排列,再枚举选不选。
对于第二个数据:枚举购买顺序。
对于第三个数据:枚举每个物品买或不买,判断这种方案是否可行,贪心判断。也可以写状压 。。
对于第四个数据:裸的背包。
对于第五个数据:装箱
对于的数据:
事实上。。。
先按从小到大排序
然后跑背包就好了。。。
证明的话。。。类比“国王游戏”邻项扰动
证明:
由于越大,越放到后面考虑时可选取的可能性越低,所以越大的理应越靠前
由于越小,越放到前面考虑时可选取的可能性越高,所以越小的理应越靠前
所以显然物品的排列顺序是需要与这两者有关的
(考场上遇到这种情况可以直接为关键字排序都试一遍,或者在代码里面都跑一遍取最优值)
如果最优的答案的取的顺序是
那dp时的顺序一定是。所以接下来是任务就是确定某个排序,使答案最大。
此时是只和与有关的。
由特殊到一般,先假设有个,就是.所以要做的就是找最优的顺序,使一开始的钱越少越好。
设
是最优的。
所以,应按从大到小排序。
证毕
/**/#include <cstdio>#include <algorithm>inline int read(){int n=0,w=1;register char c=getchar();while(c<'0' || c>'9'){if(c=='-')w=-1;c=getchar();}while(c>='0' && c<='9')n=n*10+c-'0',c=getchar();return n*w;}const int N=1001,M=5001;int n,m,dp[M];struct Node{int p,q,v;bool operator<(const Node &gg)const{return q-p<gg.q-gg.p;}}things[N];inline int max(int x,int y){return x>y?x:y;}int main(){freopen("bag.in","r",stdin);freopen("bag.out","w",stdout);n=read(),m=read();for(int i=1;i<=n;++i)things[i]=(Node){read(),read(),read()};std::sort(things+1,things+1+n);for(int i=1;i<=n;++i)for(int j=m;j>=max(things[i].p,things[i].q);--j)dp[j]=max(dp[j],dp[j-things[i].p]+things[i].v);// dfs(1,m,0);printf("%d",dp[m]);fclose(stdin),fclose(stdout);return 0;}
考虑有这样一张图
把走过的路径染色
从左上角、右下角、左下角、右上角分别进行灌水,遇到染过色的就停止
有分是给灌水的
有分是给灌水的
而满分。。。
二维离散化灌水
#include <cstdio>#include <cstring>#include <algorithm>inline int read(){int n=0,w=1;register char c=getchar();while(c<'0' || c>'9'){if(c=='-')w=-1;c=getchar();}while(c>='0' && c<='9')n=n*10+c-'0',c=getchar();return n*w;}const int N=3001;int n,px[N],py[N],x[N],y[N],Tx,Ty;char a[N][N];int nowx,nowy,sx[10000005],sy[10000005],L,R;const int F1[4]={0,0,1,-1};const int F2[4]={1,-1,0,0};inline bool judge(int x,int y){return x>=1 && x<=Tx && y>=1 && y<=Ty;}void bfs(int x,int y){if(!(1<=x&&x<=Tx&&1<=y&&y<=Ty))return;a[x][y]=1; sx[1]=x; sy[1]=y; L=1; R=1;while(L<=R){nowx=sx[L]; nowy=sy[L]; ++L;for(int xx,yy,i=0;i<4;++i){xx=nowx+F1[i],yy=nowy+F2[i];if(!a[xx][yy] && judge(xx,yy)){++R;a[xx][yy]=1;sx[R]=xx;sy[R]=yy;}}}}int main(){freopen("beng.in","r",stdin);freopen("beng.out","w",stdout);n=read();char c;for(int cx=0,cy=0,i=0;i<=n;++i){for(int j=-1;j<2;++j)x[++Tx]=cx+j,y[++Ty]=cy+j;px[i]=cx,py[i]=cy;if(i==n)break;while(c!='R' && c!='L' && c!='U' && c!='D')c=getchar();int l=read();switch(c){case 'R':cy+=l;break;case 'L':cy-=l;break;case 'U':cx-=l;break;case 'D':cx+=l;}}std::sort(x+1,x+1+Tx);Tx=std::unique(x+1,x+1+Tx)-x-1;std::sort(y+1,y+1+Ty);Ty=std::unique(y+1,y+1+Ty)-y-1;for(int i=0;i<=n;++i)px[i]=std::lower_bound(x+1,x+1+Tx,px[i])-x,py[i]=std::lower_bound(y+1,y+1+Ty,py[i])-y;for(int i=1;i<=n;++i){int sx=px[i-1],tx=px[i],sy=py[i-1],ty=py[i];if(sx>tx)std::swap(sx,tx);if(sy>ty)std::swap(sy,ty);for(int j=sx;j<=tx;++j)for(int k=sy;k<=ty;++k)a[j][k]=2;}bfs(1,1);long long ans=0;for(int i=2;i<=Tx;++i)for(int j=2;j<=Ty;++j)if(a[i][j]!=1)ans+=1ll*(x[i]-x[i-1])*(y[j]-y[j-1]);printf("%d",ans);fclose(stdin);fclose(stdout);return 0;}