• 9.16


    9.16

    疯掉——一堆ak的然鹅我只有121

    (1)shopping——反悔堆

    我的方法是直接开个原始价格优先队列和折扣价优先队列,然后每次比一比谁小就取谁,并且记录tk就是取过几次折扣价队列,超过k就不能再取。——考试时由于没有判队空导致最后一个点T飞了,由于数据水,本来可以100的

    然鹅上述非正解;hack数据

    2 1 5
    2 1
    1000 3
    out 1
    实际ans=2
    

    错误原因就是我们排序时先把优惠券给了不需要优惠的,造成后面要优惠的没劵了

    错误的ac代码

    #include <queue>
    #include <cmath>
    #include <cstdio>
    #include <vector>
    #include <cstring>
    #include <utility>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    const int N=50005;
    typedef long long LL;
    inline LL read() {
    	LL x=0;char ch=getchar();
    	while(!isdigit(ch)) ch=getchar();
    	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    	return x;
    }
    int n,k,ans;
    LL m,sum=0;
    priority_queue< pair<int,int> > p;
    priority_queue< pair<int,int> > Q;
    
    bool vis[N];
    int main() {
    	n=read();k=read();m=read();
    	for(int i=1;i<=n;i++) {
    		p.push(make_pair(-read(),i));
    		Q.push(make_pair(-read(),i));
    	}
    	int tk=1;
    	while(!p.empty()&&!Q.empty()) {
    		while(vis[p.top().second]&&p.size()) p.pop();
    		if(p.empty()) break;
    		while(vis[Q.top().second]&&Q.size()) Q.pop();
    		if(Q.empty()) break;
    		int x=-p.top().first,id1=p.top().second,xx=-Q.top().first,id2=Q.top().second;
    		if(tk<=k&&xx<x) {
    			tk++;sum+=xx;
    			vis[id2]=1;
    			Q.pop();
    			if(sum<=m) ans++;
    			else {
    				printf("%d
    ",ans);return 0;
    			}
    		} else {
    			sum+=x;
    			vis[id1]=1;
    			p.pop();
    			if(sum<=m) ans++;
    			else {
    				printf("%d
    ",ans);return 0;
    			}
    		}
    	}
    	while(tk<=k&&!Q.empty()) {
    		while(vis[Q.top().second]&&Q.size()) Q.pop();
    		if(Q.empty()) break;
    		int xx=-Q.top().first,id2=Q.top().second;
    		tk++;sum+=xx;
    		vis[id2]=1;
    		Q.pop();
    		if(sum<=m) ans++;
    		else {
    			printf("%d
    ",ans);return 0;
    		}		
    	}
    	while(!p.empty()) {
    		while(vis[p.top().second]&&p.size()) 
    			p.pop();
    		if(p.empty()) break;
    		int x=-p.top().first,id1=p.top().second;
    		sum+=x;p.pop();
    		vis[id1]=1;
    		if(sum<=m) ans++;
    		else {
    			printf("%d
    ",ans);return 0;
    		}		
    	}
    	printf("%d
    ",ans);
    	
    	return 0;
    }
    
    /*
    4 1 7 
    3 2 
    2 2 
    8 1 
    4 3
    */
    

    正解

    先贪心取折扣价前k小的,然后取原始价的堆,维护反悔堆,当sum+p[i]>m&&sum+c[i]<=m时 ,我们可以从前k小的反悔一个,把劵给这个,那个用原价

    #include <queue>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    const int N=50005;
    typedef long long LL;
    inline LL read() {
    	LL x=0;char ch=getchar();
    	while(!isdigit(ch))ch=getchar();
    	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    	return x;
    }
    int n,k,ans;
    LL m,sum=0;
    struct node{
    	int p,c;
    }a[N]; 
    bool cmp1(node a,node b) {
    	return a.c==b.c?a.p<b.p:a.c<b.c;
    }
    bool cmp2(node a,node b) {
    	return a.p<b.p;
    }
    priority_queue< int,vector<int>,greater<int> >q;
    int main() {
    	n=read();k=read();m=read();
    	for(int i=1;i<=n;i++)
    		a[i].p=read(),a[i].c=read();
    	sort(a+1,a+1+n,cmp1);
    	for(int i=1;i<=k;i++)
    		if(sum+a[i].c<=m) 
    			sum+=a[i].c,ans++,q.push(a[i].p-a[i].c);
    	sort(a+1+k,a+1+n,cmp2);//k+1到n这些按p从小到大排序
    	for(int i=k+1;i<=n;i++) {
    		int x=a[i].p,flag=0;
    		if(!q.empty()&&a[i].c+q.top()<x)
    			flag=1,x=a[i].c+q.top();
    		if(sum+x<=m) {
    			sum+=x;ans++;
    			if(flag) q.pop(),q.push(a[i].p-a[i].c);
    		}
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
    
    
    

    (2) tree——树形dp(传言贪心都能过)

    简直了一开始发现性质一条边可以用两只企鹅站,这样的一条点对,越多越好;以为是二分图最大匹配,搞了半天发现假了,然后手模发现好像是个dfs(没想树形dP),本来想写个贪心,但时间不够再次挂挂挂,最后悲惨20分

    啊啊啊想的和答案真的非常接近

    dp[i][0]表示以 i 为根的子树中能够两两配对的最大点数,不包含节点 i。

    树形dp真的蛮简单

    0表示不选它,1表示选它

    详细看代码注释

    #include <queue>
    #include <cmath>
    #include <cstdio>
    #include <vector>
    #include <cstring>
    #include <utility>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    const int N=100005;
    inline int read() {
    	int x=0;char ch=getchar();
    	while(!isdigit(ch)) ch=getchar();
    	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    	return x;
    }
    inline void Max(int &x,int y){if(x<y)x=y;}
    inline void Min(int &x,int y){if(x>y)x=y;}
    int n,T,k;
    int hd[N],to[N<<1],nxt[N<<1],tot;
    inline void add(int x,int y) {
    	to[++tot]=y;nxt[tot]=hd[x];hd[x]=tot;
    }
    int f[N][2],son[N];
    void dfs(int x,int fa) {
    	bool flag=0;int mx=-0x3f3f3f3f;
    	for(int i=hd[x];i;i=nxt[i]) {
    		int y=to[i];
    		if(y==fa) continue;
    		dfs(y,x);
    		f[x][0]+=f[y][1];
    		if(son[x]) {//很多儿子,我们先假设不选x-y的边,也就是先假设儿子都选
    			flag=1;
    			f[x][1]+=f[y][1];
    			mx=max(mx,f[y][0]-f[y][1]);//找出最大的不选这个儿子的值
    		} 
    	}
    	if(flag) f[x][1]+=mx+1;//最后选这个+1
    }
    int main() {
    	T=read();
    	while(T--) {
    		memset(son,0,sizeof(son));
    		memset(hd,0,sizeof(hd));
    		memset(f,0,sizeof(f));
    		tot=0;
    		
    		n=read();k=read();
    		for(int i=2,x;i<=n;i++) {
    			x=read();add(x,i);add(i,x);
    			son[x]++;
    		}
    		dfs(1,0);
    		int sum=max(f[1][1],f[1][0]);
    		if(2*sum>=k) {
    			printf("%d
    ",(k+1)/2);
    		} else {
    			printf("%d
    ",k-sum);//sum+(k-sum*2)  
    		}
    	} 
    	return 0;
    }
    
    
    

    (3)room——bfs最短路

    其实想法很简单,就是把钥匙状压一下,然后bfs最短路——只适合边权为 1的板子大概长这样——

    	memset(dis,0x3f,sizeof(dis));
    	dis[1]=0;
    	q.push(1);
    	while(!q.empty()) {
    		int x=q.front();q.pop();
    		for(int i=hd[x];i;i=nxt[i]) {
    			int y=to[i];
    			...
                dis[y]=min(dis[x]+1,dis[y]);
                if(!vis[y]) {
                	vis[y]=1;
                	room[y]|=room[x];
                	q.push(y);
                } 
    		}		
    	}
    

    回到这道题

    我们发现对于y这个点,可能被之前的很多个x经过,然而你走路径只能从一个x走来,也就是说不能简单地每次room[y]|=room[x],因为有些钥匙是你最短路上取不到的,而你的room却记录下来了。

    所以正解是对每个dis记录其状态,可能会存在为了取钥匙多走几步的情况。

    只要经过一个点就入队,保证都被更新到——但这样如果往复经过一个点且状态不变就会往复更新,解决方法见代码

    #include <queue>
    #include <cmath>
    #include <cstdio>
    #include <vector>
    #include <cstring>
    #include <utility>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    const int N=5005;
    const int M=20005;
    inline int read() {
    	int x=0;char ch=getchar();
    	while(!isdigit(ch)) ch=getchar();
    	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    	return x;
    }
    int n,m,k,ans=0x3f3f3f3f;
    int room[N],need[M];
    int hd[N],to[M],nxt[M],tot;
    inline void add(int x,int y) {
    	to[++tot]=y;nxt[tot]=hd[x];hd[x]=tot;
    }
    struct node{
    	int id,dis,sta;
    	node(){}
    	node(int x,int y,int z):id(x),dis(y),sta(z){}
    };
    queue<int>q;
    queue<int>Q;
    int dis[N][(1<<11)];
    bool vis[N];
    int main() {
    //	freopen("room20.in","r",stdin);
    	n=read();m=read();k=read();
    	for(int i=1;i<=n;i++) 
    		for(int j=1;j<=k;j++)
    			room[i]|=(read()<<j);
    	for(int i=1,x,y;i<=m;i++) {
    		x=read();y=read();
    		add(x,y);
    		for(int j=1;j<=k;j++)
    			need[tot]|=(read()<<j);
    	}
    	memset(dis,0x3f,sizeof(dis));
    	dis[1][room[1]]=0;
    	q.push(1);Q.push(room[1]);
    	while(!q.empty()) {
    		int x=q.front();q.pop();
    		int sta=Q.front();Q.pop();
    		for(int i=hd[x];i;i=nxt[i]) {
    			int y=to[i];
    			if((sta|need[i])==sta) {
    				if(dis[y][sta|room[y]]>=100000) {//保证每个点的这个状态只被更新1次
    					dis[y][sta|room[y]]=min(dis[y][sta|room[y]],dis[x][sta]+1);
    					q.push(y);Q.push(sta|room[y]);
    				} 
    			}				
    		}		
    	}
    	for(int i=0;i<=(1<<k+1);i++)
    		ans=min(ans,dis[n][i]);
    	if(ans==dis[n+1][0]) printf("No Solution
    ");
    	else printf("%d
    ",ans);
    	return 0;
    }
    
    
  • 相关阅读:
    Yarn安装与配置
    线性代数入门
    min_25筛小记
    刷(shui)题记录 2022.2
    CF1292D Chaotic V.
    [WC2011]最大XOR和路径
    刷(shui)题记录 2022.3
    刷题记录2022.3[2]
    计算几何入门
    V8
  • 原文地址:https://www.cnblogs.com/ke-xin/p/13698892.html
Copyright © 2020-2023  润新知