暑假の刷题6

POJ3373

这题是记忆化搜索,其实记忆不记忆的我也写不来,想到可以用dp,然后想了个转移方程然后就出来了,其实这题的dp还是比较难想的,难就难在最优子结构的性质如何去体现出来,然后想了好多种方法,有区间的,有线性的,但是想了想都是不符合区间合并律和最优子结构性质的,后来发现是通过mod得出的余数来判断;

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
int k,n,f[102][10],dp[102][10000],ans[102][10000],a[102];
char s[102];
int main()
{
	while(cin >> s)
	{
		n = strlen(s);
		cin >> k;
		for(int i = 1;i <= n;i++)
		 a[i] = s[n-i]-'0';
		for(int j = 0;j <= 9;j++)
		 f[1][j] = j % k; 
		for(int i = 2;i <= n;i++)
		 for(int j = 0;j <= 9;j++)
	  	  f[i][j] = f[i-1][j]*10 % k;
	  	memset(dp,3,sizeof(dp));
		dp[0][0] = 0;  
		for(int i = 1;i <= n;i++)
		 for(int j = 0;j < k;j++)
		  for(int now = i == n ? 1:0;now <= 9;now++)
		  {
		  	int dt = now == a[i] ? 0:1;
			if(dp[i][j] > dp[i-1][(k+j-f[i][now])%k] + dt)
			{
				dp[i][j] = dp[i-1][(k+j-f[i][now])%k] + dt;
				ans[i][j] = now;	
			} 
		  }
		int now = 0;
	    for(int i = n;i > 0;i--)
	    {
	    	cout << ans[i][now];
	    	now = (k+now-f[i][ans[i][now]]) % k;
		}
		cout << endl; 
	}
 } 

上面这个代码有一点特殊就是最开始我觉得高精很难搞,但是我们发现这题的高精做法只涉及到了mod,所以我们可以把大的操作数给拆出来搞,就是这样;

然后这题有一种搜索的方法,不过我觉得这显然是比dp难想很多的,找到一篇题解讲的很详细;
原链接
它里面把方法讲的很详细;
一个是搜索的优先级,因为首先要找解,所以要保证有解先,然后应该去考虑一下解的优先级,第一重要的是修改的次数要小,因此用迭代加深的搜法来搜,然后考虑一下大小,所以先考虑比原来的数小的数,然后先从高位开始考虑;
然后记录一下不可能的状态来剪枝是吧;就是用flag来记录一下不可能的点的情况;
这个就是主要的搜索部分,别的没啥特别的

int DFS(int pos,int res_num,int m_mod){
	if(m_mod == 0 )	return 1;
	if(pos < 0 || res_num == 0) return 0;
	if(res_num <= f[pos][m_mod]) return 0;
	for(int i=pos;i>=0;i--){
		for(int j=0;j<n[i];j++){
			if(i == strlen(s) - 1 && j == 0 )
				continue;
			int res = (m_mod - (mod[i][m[i]] - mod[i][j]) + k ) % k;
			int tmp = m[i];
			m[i] = j;
			if(DFS(pos-1,res_num-1,res)) 	return 1;
			m[i] = tmp;
		}
	}
	
	for(int i=0;i<=pos;i++){
		for(int j=n[i]+1;j<=9;j++){
			if(i == strlen(s) - 1&& j ==0 )	continue;
			int res = (m_mod - (mod[i][m[i]] - mod[i][j]) ) % k;
			int tmp = m[i];
			m[i] = j;
			if(DFS(pos-1,res_num-1,res)) return 1;
			m[i] = tmp;
		}
	}
	f[pos][m_mod] = res_num;
	return 0;
}

POJ1691

这题就是一个dag,狗都看得出来,不知道为什么要打上一个记忆化搜索的tag,我TM的想dp想了一个小时没想出来状态表示怎么搞;
然后做法就是先把dag建图给建出来,然后找一下deg入度为0的点,从那个点开始深搜,如果说有颜色相同的一直往下搜就好了,如果说他的下面既有颜色相同的又有颜色不同的话那就必须先搜颜色相同的,搜完之后并且在那一层就不要递归到颜色不同的那一层去了,一直到如果说没路了重新遍历,找到一个颜色相同的位置继续搜,颜色不同的就别管了;如果说颜色相同的一个都没有了,再去考虑颜色不同的,开始向下搜;
这样就能保证答案是最小的;

POJ1724

今晚来不及写了,做之前盲猜一手,就是spfa改版,众所周知dp是在一张dag上面的暴力,那么换成图之后应该就要变成spfa来代替它的工作,开个数组dist[i][w]表示到达第i个点需要w的价钱时候的最小值,没错就是这么做的,但是这题贼卡常,找了个正解我发现,这题我跟他唯一的不同就是链式前向星我用的是数组他用的是结构体!!
以下是轻微超时代码;

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
using namespace std;
typedef pair<int,int> PII;
const int N = 110,M = 10005;
int h[N],e[M],ne[M],l[M],t[M];
int dist[N][M],vis[N][M];
int n,k,r,idx;
void add(int a,int b,int c,int d) {
	e[idx] = b, l[idx] = c, t[idx] = d, ne[idx] = h[a], h[a] = idx ++ ; 
}
void spfa() {
	memset(dist,0x3f,sizeof dist);
	queue <PII> q ;
	for(register int i=0;i<=k;i++)	dist[1][i] = 0;  
	q.push(make_pair(1,0));
	vis[1][0] = true;
	while(q.size() ) {
		PII tmp = q.front();	q.pop();
		int u = tmp.first , c = tmp.second;
		vis[u][c] = false;
		for(register int i=h[u];~i;i=ne[i]) {
			int j = e[i],w = l[i], cost = t[i];

			if(c + cost <= k && dist[j][c+cost] > dist[u][c] + w){
				dist[j][c+cost] = dist[u][c] + w;
				q.push(make_pair(j,c+cost));	vis[j][c+cost] = true;
			}
		}
	}
}
int main()
{
	
	memset(h,-1,sizeof h);
	
	scanf("%d%d%d",&k,&n,&r);
	int s,d,tmp_l,tmp_t;
	for(int i=1;i<=r;i++){
		scanf("%d%d%d%d",&s,&d,&tmp_l,&tmp_t);
		add(s,d,tmp_l,tmp_t);
	}
	
	
//	pre_spfa();
	spfa();
	
//	for(int i=1;i<=n;i++){
//		printf("i:%d   pay:%d\n",i,pay[i]);
//	}
	
	
//	for(int i=1;i<=n;i++){
//		for(int j = 0; j<=k;j++){
//			printf("i:%d   j:%d   dist:%d\n",i,j,dist[i][j]);
//		}
//	}

	int ass = 0x3f3f3f3f;
	for(int i=0;i<=k;i++){
		ass = min(ass, dist[n][i]);
	}
	if(ass == 0x3f3f3f3f ) puts("-1");
	else printf("%d\n",ass);
	return 0; 
}
/*
5
6
7
1 2 2 3
2 4 3 3
3 4 2 4
1 3 4 1
4 6 2 1
3 5 2 0
5 4 3 2
*/

POJ1151

经典老题亚特兰蒂斯。。。
重写了一遍居然还花了一个多小时!
注意点,局部变量清零,数组cnt要及时改变;

POJ1765

随便抽的一道题,贼寄吧难,这时候才知道我有多菜;
就是说每个屋顶你要给他topo排序一下,排序得是从上到下的,然后一行之内也要按照顺序;
原本是想遍历一遍得到topo序的,但是想了一个小时都没想出来这怎么搞,去看题解了,我们发现一个点,就是说可以通过重载运算符的方法来实现topo排序;首先雨下落的地方是下端点,所以一般按照下端点来排序,然后想到特殊情况:
在这里插入图片描述
我们发现对于有覆盖的情况来说的话斜率什么的都不能起到作用,我们只能通过对比y1来进行比较,但是我没有想到用sort来,我想的是N^2???? 看来脑子还是有点问题;
比较之后就好了,然后我们就可以开线段树来做了,做的时候我看的那个题解分了两棵树,我觉得这样还是不错的因为看起来非常清晰明了;
代码先不写了,因为我写不出来所以写了也没用等我强一点之后再来写;

POJ3278

狗屎,POJ甚么玄学数据,被卡了,luogu和acwing上就可以过的,就是建个图然后广搜没难度;

POJ2049

这题挺寄吧难的,难就难在建图,为什么说建图很难是因为既有面积又有边,但是,这是图论题,我们抽象一下,把格子,也就是面积给抽象成是一个点,然后把边抽象成一条连接两个点的边就好了;
我发现我是傻逼,这题出题人也是傻逼;
这种题目拿出来就是纯粹寄吧恶心人用的;
我也不知道我为什么这么傻逼一样的写了个链式前向星,现在感觉尼玛就是无解;

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
const int N = 210, INF = 1 << 29, EMPTY = 0, DOOR = 1, WALL = INF;
int xa[N][N],ya[N][N];
int dist[N][N];
int dt[4][2] = {{-1,0},{1,0},{0,-1},{0,1}};
int xmax,ymax,n,m;
double sx,sy;
bool check(int x,int y) {
	if(x > 0 && x <= xmax && y > 0 && y <= ymax)  return 1;
	return 0;
}
int getvalue(int x,int y,int i) {
	if (i == 0) return ya[x-1][y];
	if (i == 1) return ya[x][y];
	if (i == 2) return xa[x][y-1];
	return xa[x][y];
}
int bfs(int tx,int ty) {
	queue<int> q;
	for(int i=1;i<=ymax;i++) {
		for(int j=1;j<=xmax;j++) {
			dist[i][j] = INF;
		}
	}
	dist[1][1] = 0;	q.push(1);	q.push(1);
	while(!q.empty()) {
		int vx = q.front(); q.pop();
		int vy = q.front(); q.pop();
		for(int i=0;i<4;i++) {
			int dx = vx + dt[i][0];
			int dy = vy + dt[i][1];
			int tmp = getvalue(vx,vy,i);
			if(check(dx,dy) && dist[dx][dy] > dist[vx][vy] + tmp ) {
				dist[dx][dy] = dist[vx][vy] + tmp;	q.push(dx);	q.push(dy);
			}
		}
	}
	return (dist[tx][ty] == INF?-1:dist[tx][ty]);
}
int main()
{
	while(scanf("%d%d",&m,&n)) {
		if(m == -1 && n == -1 ) break;
		ymax = xmax = -1;
		memset(xa,0,sizeof xa );
		memset(ya,0,sizeof ya );
		int x,y,d,t;
		for(int i=0;i<m;i++) {
			scanf("%d%d%d%d",&x,&y,&d,&t);
			if(d){
				for(int j=0;j<t;j++) ya[x][y+j+1] = WALL;
				ymax = max(y+t+1,ymax);
				xmax = max(x+1,xmax);
			} else {
				for(int j=0;j<t;j++) xa[x+j+1][y] = WALL;
				ymax = max(y+1,ymax);
				xmax = max(x+t+1,xmax);
			}
		} 
		for(int i=0;i<n;i++) {
			scanf("%d%d%d",&x,&y,&d);
			if(d) ya[x][y+1] = DOOR;
			else xa[x+1][y] = DOOR;
		}
		scanf("%lf%lf",&sx,&sy);
		if(!(sx >= 1 && sx <= 199 && sy >= 1 && sy <= 199 ))	printf("0\n");
		else printf("%d\n",bfs((int)sx+1,(int)sy+1));
	}	
	return 0;
}

这么好看的代码,当然不是我写的啦;

POJ3083

题目简单但是我很脑残因为我他妈的vis[X][Y] = TRUE忘记打了!!!!!!!
我他妈找bug找了两个小时然后还把递归改成循环来试。。

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
int n,m,T,ass;
char a[50][50];
int dist[50][50],vis[50][50];
typedef pair<int,int> PII;
int dx[4] = {1,-1,0,0};
int dy[4] = {0,0,1,-1};
int lleft[4] = {2,3,1,0};
int rright[4] = {3,2,0,1};
int s_x,s_y,e_x,e_y;
bool check(int x,int y) {
	if( x >= 1 && x <= m && y >= 1 && y <= n) return true;
	return false;
}
int bfs(int s_x,int s_y) {
	memset(dist,0x3f,sizeof dist) ;
	memset(vis,0,sizeof vis);
	queue<PII> q;
	vis[s_x][s_y] = true;
	dist[s_x][s_y] = 1;
	q.push( make_pair(s_x,s_y) );
	while(q.size()) {
		PII u = q.front();	q.pop();
		int x = u.first,	y = u.second;
		for(int i=0;i<4; i ++ ){
			int ne_x = x + dx[i], ne_y = y + dy[i];
			if( check(ne_x,ne_y) && a[ne_x][ne_y] != '#' && !vis[ne_x][ne_y]   ) {
				dist[ne_x][ne_y] = dist[x][y] + 1;
				vis[ne_x][ne_y] = true;
				q.push( make_pair(ne_x,ne_y));
				if(ne_x == e_x && ne_y == e_y ) {
					return dist[ne_x][ne_y];
				}  
			}	
		}
	}
	return -1;
}
void dfs1(int x,int y,int turn,int dist) {
	while(1) {	
		if (x == e_x && y == e_y ) {
			ass = dist; break;
		}
		int l_x = x + dx[lleft[turn]], l_y = y + dy[lleft[turn]];
		int ne_x = x + dx[turn], ne_y = y + dy[turn];
		if ( a[l_x][l_y] == '#' && ( a[ne_x][ne_y] == '.' || a[ne_x][ne_y] == 'E' ) ) {	
			dist++, x = ne_x, y = ne_y ;
		} else if (  a[l_x][l_y] == '#' && a[ne_x][ne_y] == '#'){
			turn = rright[turn];
		} else if ( (a[l_x][l_y] == '.' || a[l_x][l_y] == 'E') ) {
			x = l_x , y = l_y, turn = lleft[turn], dist ++;
		}
	
	}
}
void dfs2(int x,int y,int turn,int dist) {
	while(1) {
		if (x == e_x && y == e_y ) {
			ass = dist; break;
		}
		int l_x = x + dx[rright[turn]], l_y = y + dy[rright[turn]];
		int ne_x = x + dx[turn], ne_y = y + dy[turn];
		if ( a[l_x][l_y] == '#' && ( a[ne_x][ne_y] == '.' || a[ne_x][ne_y] == 'E' ) ) {	
			dist++, x = ne_x, y = ne_y ;
		} else if (  a[l_x][l_y] == '#' && a[ne_x][ne_y] == '#'){
			turn = lleft[turn];
		} else if ( (a[l_x][l_y] == '.' || a[l_x][l_y] == 'E') ) {
			x = l_x , y = l_y, turn = rright[turn], dist ++;
		}
	
	}
}
int main()
{
	scanf("%d",&T);
	while(T -- ) {	
		scanf("%d%d",&n,&m);
		for(int i=1;i<=m;i++) {
			scanf("%s", a[i] + 1);
		}
		for(int i=1;i<=m;i++){
			for(int j =1;j<=n;j++) {
				if ( a[i][j] == 'S' ) {
					s_x = i, s_y = j;
				} else if (a[i][j] == 'E'){
					e_x = i, e_y = j;
				}
			}	
		}
		int tmp_turn;
		if(s_x == 1) 		tmp_turn = 0;
		else if(s_x == m)	tmp_turn = 1;
		else if(s_y == 1)	tmp_turn = 2;
		else if(s_y == n) 	tmp_turn = 3;
		dfs1(s_x,s_y,tmp_turn,1) ;
		printf("%d ",ass);
		dfs2(s_x,s_y,tmp_turn,1);
		printf("%d ",ass);
		printf("%d\n",bfs(s_x,s_y) );
	}
}
/*

2
8 8
########
#......#
#.####.#
#.####.#
#.####.#
#.####.#
#...#..#
#S#E####
9 5
#########
#.#.#.#.#
S.......E
#.#.#.#.#
#########
*/

POJ3259

你妈的居然没看出来求负环,感觉这个题目也是描述不清属于是因为没有讲清楚源点到底怎么搞;
但是我知道如果说一个点被遍历很多遍基本上可以判断是有负环的,所以就搞了个经验值,不要吝啬调大点就好了,然后剩下一个点,然后发现源点不一定是1????
hint算个啥???
spfa求负环还是很简单,就是有一个点需要去注意一下就是

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
const int N = 600, M = 6000;
int T,n,m,W,idx;
int h[N],w[M],e[M],ne[M];
int dist[N],vis[N],st[N];
void add(int a,int b,int c) {
	e[idx] = b ,ne[idx] = h[a], w[idx] = c, h[a] = idx ++;
}
bool spfa() {
	
	queue<int> q;
	memset(vis,0,sizeof vis);
	memset(dist,0x3f,sizeof dist);
	memset(st,0,sizeof st);
	dist[1] = 0;
	vis[1] ++ ;
	q.push(1);
	st[1] = true;
	while(q.size()) {
		int u = q.front() ;	q.pop();
		st[u] = false;
		for(int i=h[u];~i;i=ne[i]){
			int j = e[i];
			if ( vis[j] > n ) {
				puts("YES");
				return true;// 这个东西一定要放在外面不然A不了;
			}
//			printf("dist[%d]:%d    dist[%d]:%d   w:%d\n",j,dist[j],u,dist[u],w[i]);
			if(dist[j] > dist[u] + w[i]) {
				dist[j] = dist[u] + w[i];
				
				if ( vis[j] > n ) {
					puts("YES");
					return true;
				}
				vis[j] ++;
				if(!st[j]) q.push(j),st[j] = true;
			} 
		}
	}
	return false;
}
int main()
{
	scanf("%d",&T);
	while( T -- ) {
		
		memset(h,-1,sizeof h);
		idx = 0;
		
		scanf("%d%d%d",&n,&m,&W);
		int a,b,t;
		for(int i=1;i<=m;i++) {
			scanf("%d%d%d",&a,&b,&t);
			add(a,b,t);	add(b,a,t);
		}
		for(int i=1;i<=W;i++) {
			scanf("%d%d%d",&a,&b,&t);
			add(a,b,-t);
		}
	
		if( !spfa() ) puts("NO");
		
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值