写省选的题目对noip没什么大用 关键是 细节题或者是思考题比较重要 练思维自然是CF比较好了 把我见到的比较好的CF题放上来刷一刷。
就是说一个人 初始值为R 有n项工作每项工作有两种属性 a和b 当且仅当 R>=a时可以完成这项任务 且R+=b; 每项任务只能完成一次 问能否把所有工作完成。注:b可能为负。
怎么做?显然对于b>=0的工作我们按照a由小到大的顺序直接做如果有不能完成的任务的话 那便一定不能完成。考虑如何做负值的工作?
想了一下我们不能仅仅按照a的值从大到小做 因为可能出现顺序上的错误 导致答案错误 按照 ai+bi从大到小的顺序么? 经过我的一番操作 发现wa掉了。再次考虑这个顺序?
苦苦思索好多天 发现没认真读题 题目中说道对于完成每一个任务都R都得非负,最后我没判断R的正负... 所以就是按照ai+bi从大到小的顺序的。
当时考的时候我直接想到了 但是没想证明这里来利用微扰法来证明一下。设当前任务排序为 a1+b1,a2+b2...an+bn;
如果对于 ai+bi >aj+bj 来说j排在i前面那么 则有R>=aj 且R-=bj 之后可得 R>=ai; 我们看另一种情况 R>=ai 且 R-=bi 之后又 R>=aj;
好像不太好证明 因为看起来非常的显然 也容易想到,那么不证自明好了.../cy
//#include<bits/stdc++.h> #include<iostream> #include<iomanip> #include<ctime> #include<cstring> #include<string> #include<ctime> #include<cctype> #include<cstdio> #include<utility> #include<queue> #include<stack> #include<deque> #include<map> #include<set> #include<bitset> #include<vector> #include<algorithm> #include<cstdlib> #define ll long long using namespace std; char buf[1<<15],*fs,*ft; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline ll read() { ll x=0,f=1;char ch=getc(); while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1;ch=getc();} while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘;ch=getc();} return x*f; } const ll MAXN=210; ll n,r,top,top1,flag; struct wy { ll x,y; }t[MAXN],w[MAXN]; inline ll cmp(wy a,wy b){return a.x<b.x;} inline ll cmp1(wy a,wy b){return a.x+a.y>b.x+b.y;} int main() { //freopen("1.in","r",stdin); n=read();r=read(); for(ll i=1;i<=n;++i) { ll x,y; x=read();y=read(); if(y>=0)t[++top]=(wy){x,y}; if(y<0)w[++top1]=(wy){x,y}; } sort(t+1,t+1+top,cmp); for(ll i=1;i<=top;++i) { if(t[i].x>r){puts("NO");return 0;} r+=t[i].y; } sort(w+1,w+1+top1,cmp1); for(ll i=1;i<=top1;++i) { if(w[i].x>r){puts("NO");return 0;} r+=w[i].y; } if(r>=0)puts("YES"); else puts("NO"); return 0; }
LINK:加强版
现在是求 我们最多能完成的任务数 这就很有区别了首先对于b>=0我们不一定能全部做完但是还是可以贪心的去做 因为拿不到的不管采取什么方法都拿不到。
考虑如何取 a<0的部分这次我们不能再贪心的选取了 因为 可能当前是能选导致R大大减小 以至于后面选的任务更少了。
但是我们仍可以按照上一道题的排序方式来选取,这样仍可以使得利益最大化...固定了顺序接下来的操作其实就是对于每个任务选或不选了,直接背包的拿取即可。dp一下...
当然和背包不太一样因为 有ai的限制我们状态设为 到第i个任务此时R值为j所做的最多的任务数,这样进行转移即可。只不过这个需要刷表法更容易写。
//#include<bits/stdc++.h> #include<iostream> #include<iomanip> #include<ctime> #include<cstring> #include<string> #include<ctime> #include<cctype> #include<cstdio> #include<utility> #include<queue> #include<stack> #include<deque> #include<map> #include<set> #include<bitset> #include<vector> #include<algorithm> #include<cstdlib> #define ll long long using namespace std; char buf[1<<15],*fs,*ft; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline int read() { int x=0,f=1;char ch=getc(); while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1;ch=getc();} while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘;ch=getc();} return x*f; } const int MAXN=110,maxn=30010<<1; int n,r,top,top1,ans,maxx; int f[MAXN][maxn];//f[i][j]表示到了第i个任务此时还有j所能完成的最大任务数 struct wy { int x,y; }t[MAXN],w[MAXN]; inline int cmp(wy a,wy b){return a.x<b.x;} inline int cmp1(wy a,wy b){return a.x+a.y>b.x+b.y;} int main() { //freopen("1.in","r",stdin); n=read();r=read(); for(int i=1;i<=n;++i) { int x,y; x=read();y=read(); if(y>=0)t[++top]=(wy){x,y}; if(y<0)w[++top1]=(wy){x,y}; } sort(t+1,t+1+top,cmp); for(int i=1;i<=top;++i) { if(t[i].x>r)break; r+=t[i].y;++ans; } sort(w+1,w+1+top1,cmp1); for(int i=1;i<=top1;++i) { for(int j=0;j<=r;++j) { f[i+1][j]=max(f[i+1][j],f[i][j]); if(j>=w[i].x&&j>=w[i].y)f[i+1][j+w[i].y]=max(f[i+1][j+w[i].y],f[i][j]+1); } } for(int j=0;j<=r;++j)maxx=max(maxx,f[top1+1][j]); printf("%d\n",maxx+ans); return 0; }
原文地址:https://www.cnblogs.com/chdy/p/11488456.html