暑假の刷题4

POJ3368

想想单调性这个条件可以怎么用,为什么说线段树不能做区间众数的题目,因为你怕的就是说这个里面出现最多的在另外一个里面一次都没有出现过是吧,但是单调性就能帮助你规避掉你的恐惧;
分成三种类型;一个st表里面,一个存的是两端点都在里面的最大出现次数,一个是左端点的颜色,另一个是右端点的颜色;
(说颜色是因为以前题目顺口了,凑合着看吧)
我们截取st婊最核心的部分分析一下具体要怎样做;

void init()
{
    for (int j = 0; j < M; j ++ )
        for (int i = 1; i + (1 << j) - 1 <= n; i ++ )
            if (!j) f[i][j] = w[i];
            else f[i][j] = max(f[i][j - 1], f[i + (1 << j - 1)][j - 1]);
            //这里就改成是两区间里面最长和交接处颜色取max
            //然后新区间的左边右边颜色都是显然的
}

int query(int l, int r)
{
    int len = r - l + 1;
    int k = log(len) / log(2);
    return max(f[l][k], f[r - (1 << k) + 1][k]);
    //如果说两边交界处颜色相同那么就比较两区间最长和交界处颜色长度;
    //else就比较两区间最长颜色就好了;
}

代码

POJ2492

和上一题可以说是差不多了,但是询问放在了最后问你有没有可疑的bug;
就是说每个敌对关系给出之前都要先搞一下,看下会不会有矛盾;
代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 2050;
int fa[N],enemy[N];
bool vis[N];
int t,n,m;
int get(int x){
	if(x != fa[x] ) return fa[x] = get(fa[x]);
	return fa[x];
}
int main()
{
	scanf("%d",&t);
	for(int i=1;i<=t;i++){
		
		memset(fa,0,sizeof fa);
		memset(enemy,0,sizeof enemy);
		
		for(int i=1;i<=n;i++){
			fa[i] = i; 
		}
		scanf("%d%d",&n,&m);
		int x,y;	bool state = true;
		while(m--){
			scanf("%d%d",&x,&y);
			
			if( !enemy[x] && !enemy[y]){
				enemy[x] = get(y);	enemy[y] = get(x);
	//			printf("x:%d   y:%d\n",x,y);
			}else if(enemy[x] && enemy[y]){
				fa[y] = get(enemy[x]);
				fa[x] = get(enemy[y]);
				enemy[x] = get(y); enemy[y] = get(x);
			}else if(enemy[x]){
				fa[y] = get(enemy[x]);//变量打错最为致命
				enemy[y] = get(x);
			}else if(enemy[y]){
				fa[x] = get(enemy[y]);
				enemy[y] = get(x);
			}
			
			if( get(x) == get(y) ) {
//				printf("x:%d   fax:%d   y:%d   fay:%d\n",x,get(x),y,get(y));
				state = false;
			}
		}
		if(state) printf("Scenario #%d:\nNo suspicious bugs found!\n\n",i);
		else printf("Scenario #%d:\nSuspicious bugs found!\n\n",i);
	}
	return 0;
}

每次AC的时候脑子里都会想去鹿哥的爆爆爆爆的神曲;
下面那个把这个方法hack了,至少不是很清晰的一种做法;
copy了另一份代码;

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int maxn = 5000;
int par[maxn];
void init(int n){
   for(int i=0;i<=n;i++)
      par[i]=i;
}
int find(int x){
    if(x!=par[x])
        par[x]=find(par[x]);
    return par[x];
}
void Union(int a,int b){
    int x=find(a);
    int y=find(b);
    if(x!=y)
       par[x]=y;
}
bool judge(int x,int y){
   x=find(x);
   y=find(y);
   if(x!=y)
    return true;
   return false;
}
int main()
{
    int t,n,m,cas=1;
    scanf("%d",&t);
    for(int i=1;i<=t;i++){
        scanf("%d%d",&n,&m);
        init(n*2);
        bool l=1;
        int x,y;
        while(m--){
            scanf("%d%d",&x,&y);
            if(judge(x,y)||judge(x+n,y+n)){
                Union(x,y+n);
                Union(x+n,y);
            }
            else
                l=0;
        }
        if(i!=1)
            puts("");
        printf("Scenario #%d:\n",cas++);
        if(l)
            printf("No suspicious bugs found!\n");
        else
            printf("Suspicious bugs found!\n");
    }
    return 0;
}

POJ1703

这个题目就是那个关押罪犯的并查集做法,关押罪犯那题个人认为是二分图去做简单点,思路非常清晰的属于是;
并查集做法就是给你一个敌对关系之后,就把enemy数组搞一下,如果敌对关系搞得时候发现他已经有enemy了,那就把他和他敌对的人的enemy合并一下,就这样就好了;

但是调不出来,网上一搜发现这个东西还有专有名词,叫种类并查集,基本思想就是,敌人的敌人就是朋友;
我发现我就是个孙笑川,我就是个龙鸣;
具体讲解看这篇B乎;
种类并查集

代码


#include<cstring>
#include<cstdio>
int set[200001],t,m,n,a,b;
int find(int n){
	if(set[n]<0)return n;
	return set[n]=find(set[n]);
}
main(){
	scanf("%d",&t);
	char c[3];
	while(t--){
		memset(set,-1,sizeof(set));
		scanf("%d%d",&n,&m);
		while(m--){
			scanf("%s%d%d",c,&a,&b);
			if(c[0]=='A'){
				if(find(a)!=find(b)&&find(a)!=find(b+n))printf("Not sure yet.\n");
				else if(find(a)==find(b))printf("In the same gang.\n");
				else printf("In different gangs.\n");	
			}
			else if(c[0]=='D'){
				if(find(a)!=find(b+n)){
					set[find(a)]=find(b+n);
					set[find(b)]=find(a+n);
				}
			}
		}
	}
}

想一想还是很对的,很巧妙啊属于是了;

POJ3096

真想一刀宰了自己,其实真的这种题目de不出来看下是不是少了或者多了个标点符号就行了,吗的;

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<map>
using namespace std;
const int N = 10010;

string s;
int ha(string sub){
	return (sub[1]-'A'+2) * (sub[1]-'A'+3) + (sub[0] - 'A'+1) *(sub[0]-'A'+4)
	+ (sub[1]-'A'+5) * (sub[0] - 'A'+6) +sub[1];
}
int main()
{
	while(cin >> s){
		if(s[0] == '*') return 0;
		if(s.length() == 1 || s.length() ==2 ) {
			cout << s;
			printf(" is surprising.\n");
			continue;
		}
		bool flag = true;
		int max_len = s.length() - 2;
		for(int len=0;len<=max_len;len++){
			map <string,int> h;
			if(!flag) break;
			for(int i=0;i + len + 1 < s.length();i++){
				int j = i + len + 1;
				if(!flag) break;
				string sub;
				sub.clear();
				sub += s[i], sub += s[j];
				if( h[sub] ) {
					cout<<s;
					printf(" is NOT surprising.\n");
					flag = false;
				} else {
					h[sub] = 1;
				}
			}
		}
		
		if(flag) {
			cout << s;
			printf(" is surprising.\n");
		}
		s.clear();
	}
	return 0;
}

POJ3007

暴力,没了,就是一个模拟题;
网上找了好久发现C++9根本不存在能替代字符串哈希的**方法,对此,至高无上的ccf,鲁迅曾经说过:世人皆可平安,唯独你🐴不行;

代码暂时放这里先;

#include<iostream>
#include<cstdio>
#include<string>
#include<map>
#include<algorithm>
using namespace std;
const int prime = 131,N=1010;
int p[N];
int t,cnt;
string s;
int main()
{
	prework();
	
	scanf("%d",&t);
	while(t--){
		
		cnt = 0;
		map <string,int> map;
		cin >> s;
		
		for(int i=0;i<s.length();i++){
			string fr = s.substr(0,i-0+1);
			string ba = s.substr(i+1);
			string re_fr = fr;
			reverse(re_fr.begin(),re_fr.end());
			string re_ba = ba;
			reverse(re_ba.begin(),re_ba.end());
			
			string tmp;
			tmp += fr+ba;   if(!map[tmp]) map[tmp] = 1,cnt++;	tmp.clear();
			tmp += fr+re_ba;if(!map[tmp]) map[tmp] = 1,cnt++;	tmp.clear();
			tmp += re_fr+ba;if(!map[tmp]) map[tmp] = 1,cnt++;	tmp.clear();
			tmp += re_fr+re_ba;if(!map[tmp]) map[tmp] = 1,cnt++;tmp.clear();
			tmp += ba+fr;if(!map[tmp]) map[tmp] = 1, cnt++;		tmp.clear();
			tmp += ba+re_fr;if(!map[tmp]) map[tmp] = 1,cnt++;	tmp.clear();
			tmp += re_ba+fr;if(!map[tmp]) map[tmp] = 1,cnt++;	tmp.clear();
			tmp += re_ba+re_fr;if(!map[tmp]) map[tmp] = 1,cnt++;tmp.clear();
		}
		printf("%d\n",cnt);
	}
	return 0;
} 
/*
1
aaa
*/

POJ1109

如果说这题目给的体积小一点或者说给一个1GB的空间的话就可以用DP来做了,DP的话感觉还是挺好想的,只有一层或者是两层或者是三层的话就可以直接搞一下暴力搜出来就好了,层数越多的话对DP越好,因为最底下那个的最大体积就能反比例函数的减小,然后大概4层之后做一下DP,层数最多应该是20,底下那个最大的H应该是80,r的话最大应该是20,乘一下发现发现空间还是挺大的,那没办法了,应该要拿来爆搜了;

每层都去考虑一下r和h的枚举范围,显然体积出来之后除以一下层数就是每一层的体积下限,r和h的下限都是这一层到m层的层数也就是距离,r与h的上限就是指在他下面一层的那啥减去一个1,如果说其中有一个是下界比上界还大的话那就直接return一下直接退出寻找,如果说体积的上界也小于体积的下界那么也直接退出好了;
体积是一定的,看下对体积的贡献我们发现R的贡献比H多一个数量级,而对于表面积的贡献里面R与H是同一个数量级的, 那么我们应该要让R比较大,而H比较小这样子更容易找到最优解;
如果说你发现这种方法目前产生的表面积已经无了那就直接退出就行了,至此我们已经集齐了三种剪枝方法,还有一个等效冗余我觉得是不好搞了;

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
using namespace std;
#define debug puts("fuckccf")
const int N = 30;
int n,m,ass = 0x3f3f3f3f;
void dfs(int deep,int vol,int last_r,int last_h,int tot){
	
	if(tot >= ass)	return;
	if(vol > n) 	return;
	
//	printf("deep:%d   vol:%d   lastr:%d   lasth:%d   tot:%d\n",
//	deep,vol,last_r,last_h,tot);
	
	if(deep == m + 1) {
		if(vol == n)
		ass = min(ass,tot);
		return;
	}
	
	
	
	int min_vol = (n - vol) / (m-deep+1);
	
	for(int r = last_r-1; r >= m-deep+1;r--){
		
		if( r*r*(last_h-1) < min_vol) break;
		
		for(int h=last_h-1; h>=m-deep+1;h--){
			int tmp_vol = r*r*h;
			if(tmp_vol < min_vol) break;
			int spe = 0;
			if(deep == 1) spe = r*r; 
//			if(deep == 2 && last_r == 4 && last_h == 6 ) {
//				printf("h:%d     r:%d   vol:%d\n",h,r,tmp_vol);
//			}
			dfs(deep+1,vol + tmp_vol,r,h,tot+2*r*h+spe);
		}
	}
}	
int main()
{	
	scanf("%d%d",&n,&m);
	dfs(1,0,100,200,0);
	printf("%d\n",ass);
	return 0;
}	
/*
100
2
*/

出题人的马暂时还比较安全,我只能说芜湖起飞;

POJ3411

别说这题还真的挺有意思啊,数据范围神他妈那么小;

如果说这题给的是一张dag的话就可以直接dp了,因为体现在这个题目里面的阶段性也好还是什么最优子结构性质也好都是比较明显的可以发现的是吧,那么既然不是dag,那么我们就用单元最短路倞来做;
首先我们把那种c = a 或者是c = b 的边还有就是在b直接付钱比较实惠的边,给敲定下来,这样子的话让一些边只有一条边权了,然后跑两遍弗洛伊德,第一遍求的是纯粹的距离,第二遍求的是真真实的最短距离;
原来想用DJ和SPFA来搞得,吗的想了半个小时不出来我是服了,好像用这两个也不好搞,因为要快速求出来任意两点之间的距离;

真的,我是傻逼,样例还没推完就开始瞎想,其实这题还存在一个能走到一个点再走回来这样的骚操作,就这而言DP是做不了的;因为这已经是不满足最优子结构的性质了,也就是说,如果用单元最短路倞的方法去搜路径的话其实也是要满足最优子结构性质的;
只能搜索了;

终于A了,这么小的一个图他妈的还有重边,出题的是想吃屎吗,我把你爸jb拽下来摇花手摇到你爸飞天遁地走;

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N = 15;
int n,m,idx,ass = 0x3f3f3f3f;
int vis[N];
int h[N],e[N],ne[N],p[N],r[N],cc[N];
void add(int a,int b,int c,int d,int k){
	e[idx] = b, p[idx] = c, r[idx] = d,cc[idx] = k,ne[idx] = h[a],h[a] = idx++;
}
void dfs(int u,int dis){
	if(u == n){
		ass = min(ass,dis);
		return;
	}
	if(dis >= ass) return;
//	printf("u:%d\n",u);
	
	vis[u] ++;

	for(int i = h[u];~i;i=ne[i]){
		int j = e[i];
		
		if(vis[j] >= n) continue;
		
		int new_dis = dis+r[i];
		
		if ( vis[cc[i]] ) new_dis = min(new_dis,dis + p[i]);
		
		dfs(j,new_dis);
	}
	
	vis[u] --;
	
}
int main()
{
	memset(h,-1,sizeof h);
	
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++){
		int a,b,c,pi,ri;
		scanf("%d%d%d%d%d",&a,&b,&c,&pi,&ri);
		add(a,b,pi,ri,c);
	}
	
	dfs(1,0);
	if(ass != 0x3f3f3f3f)	printf("%d\n",ass);
	else printf("impossible\n");
	return 0;
}
/*
4 5
1 2 1 10 10
2 3 1 30 50
3 4 3 80 80
2 1 2 10 10
1 3 2 10 50
*/
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值