洛谷 2731 骑马修栅栏
题目
给定一个无向图,找到一条路径,使它经过无向图的所有边,并且字典序最小
分析
这是一个一笔画的问题,考虑找到奇点,从奇点开始深搜,没、每经过一条边删掉一次
代码
/*
ID:lemondi1
LANG:C++
TASK:fence
*/
#include <cstdio>
#include <cctype>
#include <stack>
#define rr register
using namespace std;
int mp[501][501],deg[501],root; stack<int>q;
inline signed iut(){
rr int ans=0; rr char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
return ans;
}
inline void print(int ans){
if (ans>9) print(ans/10);
putchar(ans%10+48);
}
inline void dfs(int x){
for (rr int i=1;i<=500;++i)
if (mp[x][i]){
--mp[x][i],--mp[i][x];
dfs(i);
}
q.push(x);
}
signed main(){
freopen("fence.in","r",stdin);
freopen("fence.out","w",stdout);
for (rr int m=iut();m;--m){
rr int x=iut(),y=iut();
++mp[x][y],++mp[y][x],
++deg[x],++deg[y];
}
for (rr int i=1;i<=500;++i)
if (deg[i]) {root=i; break;}
for (rr int i=1;i<=500;++i)
if (deg[i]&1) {root=i; break;}
dfs(root);
while (q.size()) print(q.top()),putchar(10),q.pop();
return 0;
}
洛谷 2732 商品购物
题目
有最多5种商品,每种商品都有花费和所需的数量,有些商品的组合能有更优惠的价格,问买完所需数量商品的最小花费
分析
完全背包,不多说
代码
/*
ID:lemondi1
LANG:C++
TASK:shopping
*/
#include <cstdio>
#include <cstring>
#include <cctype>
#define rr register
using namespace std;
int n,cnt,rk[1007],ned[107][6],fwe[107],dp[6][6][6][6][6],nwe[6],amo[6];
inline signed iut(){
rr int ans=0; rr char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
return ans;
}
inline void Mn(int &a,int b){a=a<b?a:b;}
signed main(){
freopen("shopping.in","r",stdin);
freopen("shopping.out","w",stdout);
n=iut(),memset(rk,-1,sizeof(rk));
for (rr int i=0;i<n;++i){
rr int tot=iut();
for (rr int j=0;j<tot;++j){
rr int now1=iut(),now2=iut();
if (rk[now1]==-1) rk[now1]=cnt++;
ned[i][rk[now1]]=now2;
}
fwe[i]=iut();
}
for (rr int i=iut();i;--i){
rr int now1=iut(),now2=iut(),w=iut();
if (rk[now1]==-1) rk[now1]=cnt++;
nwe[rk[now1]]=w,amo[rk[now1]]=now2;
}
for (rr int i=0;i<=amo[0];++i)
for (rr int j=0;j<=amo[1];++j)
for (rr int k=0;k<=amo[2];++k)
for (rr int p=0;p<=amo[3];++p)
for (rr int h=0;h<=amo[4];++h){
rr int &t=dp[i][j][k][p][h]=i*nwe[0]+j*nwe[1]+k*nwe[2]+p*nwe[3]+h*nwe[4];
for (rr int now=0;now<n;++now)
if (i>=ned[now][0]&&j>=ned[now][1]&&k>=ned[now][2]&&p>=ned[now][3]&&h>=ned[now][4])
Mn(t,dp[i-ned[now][0]][j-ned[now][1]][k-ned[now][2]][p-ned[now][3]][h-ned[now][4]]+fwe[now]);
}
return !printf("%d\n",dp[amo[0]][amo[1]][amo[2]][amo[3]][amo[4]]);
}
洛谷 1930 亚瑟王的宫殿
题目
分析
考虑答案也就是
n
−
1
n-1
n−1个骑士到集合点,剩下一个骑士先到与国王的会合点再到集合点的距离,可以发现,国王到某个点的距离就是切比雪夫距离,
首先对于每个点都对外跑一遍bfs,然后枚举集合点,再枚举与国王会合的骑士,再枚举他们的汇合点,时间复杂度
O
(
R
3
C
3
)
O(R^3C^3)
O(R3C3),实际远低于这一时间复杂度,而且可以玄学地选择国王周围5*5的位置作为汇合点(虽然没有正确性)
代码
/*
ID:lemondi1
LANG:C++
TASK:camelot
*/
#include <iostream>
#include <queue>
#include <cstring>
#define rr register
using namespace std;
const int dx[8]={-1,-1,-2,-2,1,1,2,2},dy[8]={2,-2,1,-1,2,-2,1,-1};
struct rec{int x,y;}kn[1201]; queue<rec>q;
int v[41][31],dis[41][31][41][31],r,c;
inline signed max(int a,int b){return a>b?a:b;}
inline void bfs(int qx,int qy){
memset(v,0,sizeof(v));
q.push((rec){qx,qy}),v[qx][qy]=1,dis[qx][qy][qx][qy]=0;
while (q.size()){
rr int nx=q.front().x,ny=q.front().y; q.pop();
for (rr int k=0;k<8;++k){
rr int zx=nx+dx[k],zy=ny+dy[k];
if (zx<1||zx>r||zy<1||zy>c||v[zx][zy]) continue;
dis[qx][qy][zx][zy]=dis[qx][qy][nx][ny]+1;
q.push((rec){zx,zy}),v[zx][zy]=1;
}
}
}
inline signed aabs(int x){return x<0?-x:x;}
signed main(){
freopen("camelot.in","r",stdin);
freopen("camelot.out","w",stdout);
cin.tie(0),cout.tie(0),cin>>r>>c,
ios::sync_with_stdio(0); rr char roww;
memset(dis,42,sizeof(dis));
rr int colu,cnt=-1,ans=dis[0][0][0][0];
while (cin>>roww>>colu){
rr rec t=(rec){colu,roww^64};
kn[++cnt]=t;
}
for (rr int i=1;i<=r;++i)
for (rr int j=1;j<=c;++j) bfs(i,j);
for (rr int i=1;i<=r;++i)
for (rr int j=1;j<=c;++j){
rr int sum=0;
for (rr int k=1;k<=cnt;++k) sum+=dis[kn[k].x][kn[k].y][i][j];
ans=min(ans,sum+max(aabs(kn[0].x-i),aabs(kn[0].y-j)));
for (rr int k=1;k<=cnt;++k){
sum-=dis[kn[k].x][kn[k].y][i][j];
rr int lx=max(kn[0].x-2,1),ly=max(kn[0].y-2,1),rx=min(kn[0].x+2,r),ry=min(kn[0].y+2,c);
for (rr int ii=lx;ii<=rx;++ii)
for (rr int jj=ly;jj<=ry;++jj)
ans=min(ans,sum+dis[kn[k].x][kn[k].y][ii][jj]+max(aabs(ii-kn[0].x),aabs(jj-kn[0].y))+dis[ii][jj][i][j]);
sum+=dis[kn[k].x][kn[k].y][i][j];
}
}
cout<<ans<<endl;
return 0;
}
洛谷 2733 家的范围
题目
问一个01矩阵中有多少个边长超过1的正方形(超简化)
分析
dp,设 d p [ i ] [ j ] dp[i][j] dp[i][j]表示以 ( i , j ) (i,j) (i,j)为右下角的正方形的最大边长,那么 d p [ i ] [ j ] = m i n ( d p [ i − 1 ] [ j ] , d p [ i ] [ j − 1 ] , d p [ i − 1 ] [ j − 1 ] ) + 1 dp[i][j]=min(dp[i-1][j],dp[i][j-1],dp[i-1][j-1])+1 dp[i][j]=min(dp[i−1][j],dp[i][j−1],dp[i−1][j−1])+1
代码
/*
ID:lemondi1
LANG:C++
TASK:range
*/
#include <cstdio>
#include <cctype>
#define rr register
using namespace std;
int dp[251][251],n,m,ans[251];
inline void print(int ans){
if (ans>9) print(ans/10);
putchar(ans%10+48);
}
inline signed min(int a,int b){return a<b?a:b;}
inline signed mIn(int a,int b,int c){return min(min(a,b),c);}
signed main(){
freopen("range.in","r",stdin);
freopen("range.out","w",stdout);
scanf("%d",&n);
for (rr int i=1;i<=n;++i)
for (rr int j=1;j<=n;++j){
rr char c=getchar();
while (!isdigit(c)) c=getchar();
if (c^49) continue;
dp[i][j]=mIn(dp[i-1][j],dp[i][j-1],dp[i-1][j-1])+1;
++ans[dp[i][j]];
}
for (rr int i=n;i>1;--i) ans[i-1]+=ans[i];
for (rr int i=2;i<=n;++i)
if (!ans[i]) break;
else print(i),putchar(32),print(ans[i]),putchar(10);
return 0;
}
洛谷 2734 游戏
分析
维护前缀和,可以知道,第二个人的最优方案也就是总和减去第一个人的最优方案
设
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]表示第一个人选区间
[
l
∼
r
]
[l\sim r]
[l∼r]的最优方案,那么
d
p
[
i
]
[
j
]
=
s
[
j
]
−
s
[
i
−
1
]
−
m
i
n
(
d
p
[
i
+
1
]
[
j
]
,
d
p
[
i
]
[
j
−
1
]
)
dp[i][j]=s[j]-s[i-1]-min(dp[i+1][j],dp[i][j-1])
dp[i][j]=s[j]−s[i−1]−min(dp[i+1][j],dp[i][j−1])
代码
/*
ID:lemondi1
LANG:C++
TASK:game1
*/
#include <cstdio>
#include <cctype>
#define rr register
using namespace std;
int n,s[101],dp[101][101];
inline signed iut(){
rr int ans=0; rr char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
return ans;
}
inline signed min(int a,int b){return a<b?a:b;}
signed main(){
freopen("game1.in","r",stdin);
freopen("game1.out","w",stdout);
n=iut();
for (rr int i=1;i<=n;++i)
s[i]=s[i-1]+(dp[i][i]=iut());
for (rr int i=n-1;i>=1;--i)
for (rr int j=i+1;j<=n;++j)
dp[i][j]=s[j]-s[i-1]-min(dp[i+1][j],dp[i][j-1]);
return !printf("%d %d\n",dp[1][n],s[n]-dp[1][n]);
}